analyzer: handle __attribute__((const)) [PR104434]

When testing -fanalyzer on openblas-0.3, I noticed slightly over 2000
false positives from -Wanalyzer-malloc-leak on code like this:

        if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
            pt_t = (lapack_complex_float*)
                LAPACKE_malloc( sizeof(lapack_complex_float) *
                                ldpt_t * MAX(1,n) );
            [...snip...]
        }

        [...snip lots of code...]

        if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
            LAPACKE_free( pt_t );
        }

where LAPACKE_lsame is a char-comparison function implemented in a
different TU.
The analyzer naively considers the execution path where:
  LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' )
is true at the malloc guard, but then false at the free guard, which
is thus a memory leak.

This patch makes -fanalyer respect __attribute__((const)), so that the
analyzer treats such functions as returning the same value when given
the same inputs.

I've filed https://github.com/xianyi/OpenBLAS/issues/3543 suggesting that
LAPACKE_lsame be annotated with __attribute__((const)); with that, and
with this patch, the false positives seem to be fixed.

gcc/analyzer/ChangeLog:
	PR analyzer/104434
	* analyzer.h (class const_fn_result_svalue): New decl.
	* region-model-impl-calls.cc (call_details::get_manager): New.
	* region-model-manager.cc
	(region_model_manager::get_or_create_const_fn_result_svalue): New.
	(region_model_manager::log_stats): Log
	m_const_fn_result_values_map.
	* region-model.cc (const_fn_p): New.
	(maybe_get_const_fn_result): New.
	(region_model::on_call_pre): Handle fndecls with
	__attribute__((const)) by calling the above rather than making
	a conjured_svalue.
	* region-model.h (visitor::visit_const_fn_result_svalue): New.
	(region_model_manager::get_or_create_const_fn_result_svalue): New
	decl.
	(region_model_manager::const_fn_result_values_map_t): New typedef.
	(region_model_manager::m_const_fn_result_values_map): New field.
	(call_details::get_manager): New decl.
	* svalue.cc (svalue::cmp_ptr): Handle SK_CONST_FN_RESULT.
	(const_fn_result_svalue::dump_to_pp): New.
	(const_fn_result_svalue::dump_input): New.
	(const_fn_result_svalue::accept): New.
	* svalue.h (enum svalue_kind): Add SK_CONST_FN_RESULT.
	(svalue::dyn_cast_const_fn_result_svalue): New.
	(class const_fn_result_svalue): New.
	(is_a_helper <const const_fn_result_svalue *>::test): New.
	(template <> struct default_hash_traits<const_fn_result_svalue::key_t>):
	New.

gcc/testsuite/ChangeLog:
	PR analyzer/104434
	* gcc.dg/analyzer/attr-const-1.c: New test.
	* gcc.dg/analyzer/attr-const-2.c: New test.
	* gcc.dg/analyzer/attr-const-3.c: New test.
	* gcc.dg/analyzer/pr104434-const.c: New test.
	* gcc.dg/analyzer/pr104434-nonconst.c: New test.
	* gcc.dg/analyzer/pr104434.h: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-02-23 09:14:58 -05:00
parent cdcea7c1ef
commit aee1adf2cd
13 changed files with 954 additions and 6 deletions

View File

@ -54,6 +54,7 @@ class svalue;
class compound_svalue;
class conjured_svalue;
class asm_output_svalue;
class const_fn_result_svalue;
typedef hash_set<const svalue *> svalue_set;
class region;
class frame_region;

View File

@ -80,6 +80,14 @@ call_details::call_details (const gcall *call, region_model *model,
}
}
/* Get the manager from m_model. */
region_model_manager *
call_details::get_manager () const
{
return m_model->get_manager ();
}
/* Get any uncertainty_t associated with the region_model_context. */
uncertainty_t *

View File

@ -1232,6 +1232,32 @@ get_or_create_asm_output_svalue (tree type,
return asm_output_sval;
}
/* Return the svalue * of type TYPE for the result of a call to FNDECL
with __attribute__((const)), given INPUTS as inputs. */
const svalue *
region_model_manager::
get_or_create_const_fn_result_svalue (tree type,
tree fndecl,
const vec<const svalue *> &inputs)
{
gcc_assert (type);
gcc_assert (fndecl);
gcc_assert (DECL_P (fndecl));
gcc_assert (TREE_READONLY (fndecl));
gcc_assert (inputs.length () <= const_fn_result_svalue::MAX_INPUTS);
const_fn_result_svalue::key_t key (type, fndecl, inputs);
if (const_fn_result_svalue **slot = m_const_fn_result_values_map.get (key))
return *slot;
const_fn_result_svalue *const_fn_result_sval
= new const_fn_result_svalue (type, fndecl, inputs);
RETURN_UNKNOWN_IF_TOO_COMPLEX (const_fn_result_sval);
m_const_fn_result_values_map.put (key, const_fn_result_sval);
return const_fn_result_sval;
}
/* Given STRING_CST, a STRING_CST and BYTE_OFFSET_CST a constant,
attempt to get the character at that offset, returning either
the svalue for the character constant, or NULL if unsuccessful. */
@ -1671,6 +1697,8 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const
log_uniq_map (logger, show_objs, "conjured_svalue", m_conjured_values_map);
log_uniq_map (logger, show_objs, "asm_output_svalue",
m_asm_output_values_map);
log_uniq_map (logger, show_objs, "const_fn_result_svalue",
m_const_fn_result_values_map);
logger->log ("max accepted svalue num_nodes: %i",
m_max_complexity.m_num_nodes);

View File

@ -1207,6 +1207,51 @@ region_model::check_call_args (const call_details &cd) const
cd.get_arg_svalue (arg_idx);
}
/* Return true if CD is known to be a call to a function with
__attribute__((const)). */
static bool
const_fn_p (const call_details &cd)
{
tree fndecl = cd.get_fndecl_for_call ();
if (!fndecl)
return false;
gcc_assert (DECL_P (fndecl));
return TREE_READONLY (fndecl);
}
/* If this CD is known to be a call to a function with
__attribute__((const)), attempt to get a const_fn_result_svalue
based on the arguments, or return NULL otherwise. */
static const svalue *
maybe_get_const_fn_result (const call_details &cd)
{
if (!const_fn_p (cd))
return NULL;
unsigned num_args = cd.num_args ();
if (num_args > const_fn_result_svalue::MAX_INPUTS)
/* Too many arguments. */
return NULL;
auto_vec<const svalue *> inputs (num_args);
for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
{
const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
if (!arg_sval->can_have_associated_state_p ())
return NULL;
inputs.quick_push (arg_sval);
}
region_model_manager *mgr = cd.get_manager ();
const svalue *sval
= mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
cd.get_fndecl_for_call (),
inputs);
return sval;
}
/* Update this model for the CALL stmt, using CTXT to report any
diagnostics - the first half.
@ -1245,10 +1290,16 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
if (tree lhs = gimple_call_lhs (call))
{
const region *lhs_region = get_lvalue (lhs, ctxt);
const svalue *sval
= m_mgr->get_or_create_conjured_svalue (TREE_TYPE (lhs), call,
lhs_region);
purge_state_involving (sval, ctxt);
const svalue *sval = maybe_get_const_fn_result (cd);
if (!sval)
{
/* For the common case of functions without __attribute__((const)),
use a conjured value, and purge any prior state involving that
value (in case this is in a loop). */
sval = m_mgr->get_or_create_conjured_svalue (TREE_TYPE (lhs), call,
lhs_region);
purge_state_involving (sval, ctxt);
}
set_value (lhs_region, sval, ctxt);
}

View File

@ -224,6 +224,7 @@ public:
virtual void visit_compound_svalue (const compound_svalue *) {}
virtual void visit_conjured_svalue (const conjured_svalue *) {}
virtual void visit_asm_output_svalue (const asm_output_svalue *) {}
virtual void visit_const_fn_result_svalue (const const_fn_result_svalue *) {}
virtual void visit_region (const region *) {}
};
@ -282,6 +283,10 @@ public:
const gasm *asm_stmt,
unsigned output_idx,
const vec<const svalue *> &inputs);
const svalue *
get_or_create_const_fn_result_svalue (tree type,
tree fndecl,
const vec<const svalue *> &inputs);
const svalue *maybe_get_char_from_string_cst (tree string_cst,
tree byte_offset_cst);
@ -436,6 +441,10 @@ private:
asm_output_svalue *> asm_output_values_map_t;
asm_output_values_map_t m_asm_output_values_map;
typedef hash_map<const_fn_result_svalue::key_t,
const_fn_result_svalue *> const_fn_result_values_map_t;
const_fn_result_values_map_t m_const_fn_result_values_map;
bool m_checking_feasibility;
/* "Dynamically-allocated" svalue instances.
@ -496,6 +505,7 @@ public:
call_details (const gcall *call, region_model *model,
region_model_context *ctxt);
region_model_manager *get_manager () const;
region_model_context *get_ctxt () const { return m_ctxt; }
uncertainty_t *get_uncertainty () const;
tree get_lhs_type () const { return m_lhs_type; }

View File

@ -540,6 +540,26 @@ svalue::cmp_ptr (const svalue *sval1, const svalue *sval2)
return 0;
}
break;
case SK_CONST_FN_RESULT:
{
const const_fn_result_svalue *const_fn_result_sval1
= (const const_fn_result_svalue *)sval1;
const const_fn_result_svalue *const_fn_result_sval2
= (const const_fn_result_svalue *)sval2;
int d1 = DECL_UID (const_fn_result_sval1->get_fndecl ());
int d2 = DECL_UID (const_fn_result_sval2->get_fndecl ());
if (int cmp_fndecl = d1 - d2)
return cmp_fndecl;
if (int cmp = ((int)const_fn_result_sval1->get_num_inputs ()
- (int)const_fn_result_sval2->get_num_inputs ()))
return cmp;
for (unsigned i = 0; i < const_fn_result_sval1->get_num_inputs (); i++)
if (int input_cmp
= svalue::cmp_ptr (const_fn_result_sval1->get_input (i),
const_fn_result_sval2->get_input (i)))
return input_cmp;
return 0;
}
}
}
@ -1892,6 +1912,59 @@ asm_output_svalue::accept (visitor *v) const
m_input_arr[i]->accept (v);
}
/* class const_fn_result_svalue : public svalue. */
/* Implementation of svalue::dump_to_pp vfunc for const_fn_result_svalue. */
void
const_fn_result_svalue::dump_to_pp (pretty_printer *pp, bool simple) const
{
if (simple)
{
pp_printf (pp, "CONST_FN_RESULT(%qD, {", m_fndecl);
for (unsigned i = 0; i < m_num_inputs; i++)
{
if (i > 0)
pp_string (pp, ", ");
dump_input (pp, i, m_input_arr[i], simple);
}
pp_string (pp, "})");
}
else
{
pp_printf (pp, "CONST_FN_RESULT(%qD, {", m_fndecl);
for (unsigned i = 0; i < m_num_inputs; i++)
{
if (i > 0)
pp_string (pp, ", ");
dump_input (pp, i, m_input_arr[i], simple);
}
pp_string (pp, "})");
}
}
/* Subroutine of const_fn_result_svalue::dump_to_pp. */
void
const_fn_result_svalue::dump_input (pretty_printer *pp,
unsigned input_idx,
const svalue *sval,
bool simple) const
{
pp_printf (pp, "arg%i: ", input_idx);
sval->dump_to_pp (pp, simple);
}
/* Implementation of svalue::accept vfunc for const_fn_result_svalue. */
void
const_fn_result_svalue::accept (visitor *v) const
{
v->visit_const_fn_result_svalue (this);
for (unsigned i = 0; i < m_num_inputs; i++)
m_input_arr[i]->accept (v);
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */

View File

@ -48,7 +48,8 @@ enum svalue_kind
SK_WIDENING,
SK_COMPOUND,
SK_CONJURED,
SK_ASM_OUTPUT
SK_ASM_OUTPUT,
SK_CONST_FN_RESULT
};
/* svalue and its subclasses.
@ -77,7 +78,9 @@ enum svalue_kind
compound_svalue (SK_COMPOUND): a mapping of bit-ranges to svalues
conjured_svalue (SK_CONJURED): a value arising from a stmt
asm_output_svalue (SK_ASM_OUTPUT): an output from a deterministic
asm stmt. */
asm stmt.
const_fn_result_svalue (SK_CONST_FN_RESULT): the return value from
a function with __attribute((const)) for given inputs. */
/* An abstract base class representing a value held by a region of memory. */
@ -129,6 +132,8 @@ public:
dyn_cast_conjured_svalue () const { return NULL; }
virtual const asm_output_svalue *
dyn_cast_asm_output_svalue () const { return NULL; }
virtual const const_fn_result_svalue *
dyn_cast_const_fn_result_svalue () const { return NULL; }
tree maybe_get_constant () const;
const region *maybe_get_region () const;
@ -1535,4 +1540,128 @@ template <> struct default_hash_traits<asm_output_svalue::key_t>
{
static const bool empty_zero_p = true;
};
namespace ana {
/* The return value from a function with __attribute((const)) for given
inputs, provided that we don't have too many inputs, and all of them
are deterministic.
Comparisons of variables that share the same const_fn_result_svalue are known
to be equal, even if we don't know what the value is. */
class const_fn_result_svalue : public svalue
{
public:
/* Imposing an upper limit and using a (small) array allows key_t
to avoid memory management. */
static const unsigned MAX_INPUTS = 2;
/* A support class for uniquifying instances of const_fn_result_svalue. */
struct key_t
{
key_t (tree type,
tree fndecl,
const vec<const svalue *> &inputs)
: m_type (type), m_fndecl (fndecl),
m_num_inputs (inputs.length ())
{
gcc_assert (inputs.length () <= MAX_INPUTS);
for (unsigned i = 0; i < m_num_inputs; i++)
m_input_arr[i] = inputs[i];
}
hashval_t hash () const
{
inchash::hash hstate;
hstate.add_ptr (m_type);
hstate.add_ptr (m_fndecl);
for (unsigned i = 0; i < m_num_inputs; i++)
hstate.add_ptr (m_input_arr[i]);
return hstate.end ();
}
bool operator== (const key_t &other) const
{
if (!(m_type == other.m_type
&& m_fndecl == other.m_fndecl
&& m_num_inputs == other.m_num_inputs))
return false;
for (unsigned i = 0; i < m_num_inputs; i++)
if (m_input_arr[i] != other.m_input_arr[i])
return false;
return true;
}
/* Use m_fndecl to mark empty/deleted. */
void mark_deleted () { m_fndecl = reinterpret_cast<tree> (1); }
void mark_empty () { m_fndecl = NULL; }
bool is_deleted () const
{
return m_fndecl == reinterpret_cast<tree> (1);
}
bool is_empty () const { return m_fndecl == NULL; }
tree m_type;
tree m_fndecl;
unsigned m_num_inputs;
const svalue *m_input_arr[MAX_INPUTS];
};
const_fn_result_svalue (tree type,
tree fndecl,
const vec<const svalue *> &inputs)
: svalue (complexity::from_vec_svalue (inputs), type),
m_fndecl (fndecl),
m_num_inputs (inputs.length ())
{
gcc_assert (inputs.length () <= MAX_INPUTS);
for (unsigned i = 0; i < m_num_inputs; i++)
m_input_arr[i] = inputs[i];
}
enum svalue_kind get_kind () const FINAL OVERRIDE
{
return SK_CONST_FN_RESULT;
}
const const_fn_result_svalue *
dyn_cast_const_fn_result_svalue () const FINAL OVERRIDE
{
return this;
}
void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
void accept (visitor *v) const FINAL OVERRIDE;
tree get_fndecl () const { return m_fndecl; }
unsigned get_num_inputs () const { return m_num_inputs; }
const svalue *get_input (unsigned idx) const { return m_input_arr[idx]; }
private:
void dump_input (pretty_printer *pp,
unsigned input_idx,
const svalue *sval,
bool simple) const;
tree m_fndecl;
unsigned m_num_inputs;
const svalue *m_input_arr[MAX_INPUTS];
};
} // namespace ana
template <>
template <>
inline bool
is_a_helper <const const_fn_result_svalue *>::test (const svalue *sval)
{
return sval->get_kind () == SK_CONST_FN_RESULT;
}
template <> struct default_hash_traits<const_fn_result_svalue::key_t>
: public member_function_hash_traits<const_fn_result_svalue::key_t>
{
static const bool empty_zero_p = true;
};
#endif /* GCC_ANALYZER_SVALUE_H */

View File

@ -0,0 +1,152 @@
/* Verify that we handle functions with __attribute__ ((const)) correctly. */
#include "analyzer-decls.h"
extern int nonconst_fn (int);
extern int const_fn_0 () __attribute__ ((const));
extern int const_fn_1 (int) __attribute__ ((const));
extern int const_fn_2 (int, int) __attribute__ ((const));
extern int const_fn_3 (int, int, int) __attribute__ ((const));
extern int const_fn_variadic (int, ...) __attribute__ ((const));
/* Verify that functions without __attribute__ ((const)) have a different
result each time. */
void test_nonconst_fn (int x, int y)
{
int x_1 = nonconst_fn (x);
int x_2 = nonconst_fn (x);
int y_1 = nonconst_fn (y);
int y_2 = nonconst_fn (y);
__analyzer_eval (x_1 == x_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (y_1 == y_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (x_1 == y_1); /* { dg-warning "UNKNOWN" } */
}
/* Verify functions with __attribute__ ((const)) have the same result
for the same arguments. */
/* 0 args. */
extern int other_const_fn_0 () __attribute__ ((const));
void test_const_fn_0 (void)
{
int a = const_fn_0 ();
int b = const_fn_0 ();
int c = other_const_fn_0 ();
int d = other_const_fn_0 ();
__analyzer_eval (a == b); /* { dg-warning "TRUE" } */
__analyzer_eval (c == d); /* { dg-warning "TRUE" } */
__analyzer_eval (a == c); /* { dg-warning "UNKNOWN" } */
}
/* 1 arg. */
void test_const_fn_1 (int x, int y)
{
int x_1 = const_fn_1 (x);
int x_2 = const_fn_1 (x);
int y_1 = const_fn_1 (y);
int y_2 = const_fn_1 (y);
__analyzer_eval (x_1 == x_2); /* { dg-warning "TRUE" } */
__analyzer_eval (y_1 == y_2); /* { dg-warning "TRUE" } */
__analyzer_eval (x_1 == y_1); /* { dg-warning "UNKNOWN" } */
}
/* 2 args. */
void test_const_fn_2 (int x, int y, int p, int q)
{
int xy_1 = const_fn_2 (x, y);
int xy_2 = const_fn_2 (x, y);
int pq_1 = const_fn_2 (p, q);
int pq_2 = const_fn_2 (p, q);
__analyzer_eval (xy_1 == xy_2); /* { dg-warning "TRUE" } */
__analyzer_eval (pq_1 == pq_2); /* { dg-warning "TRUE" } */
__analyzer_eval (xy_1 == pq_1); /* { dg-warning "UNKNOWN" } */
}
/* We don't handle above 2 args. */
void test_const_fn_3 (int x, int y, int z, int p, int q, int r)
{
int xyz_1 = const_fn_3 (x, y, z);
int xyz_2 = const_fn_3 (x, y, z);
int pqr_1 = const_fn_3 (p, q, r);
int pqr_2 = const_fn_3 (p, q, r);
__analyzer_eval (xyz_1 == xyz_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (pqr_1 == pqr_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (xyz_1 == pqr_1); /* { dg-warning "UNKNOWN" } */
}
/* Variadic fn, with various numbers of extra args. */
void test_const_fn_variadic (int x, int y, int z, int p, int q, int r)
{
/* 0 extra args, for 1 arg in total. */
int x_1 = const_fn_variadic (x);
int x_2 = const_fn_variadic (x);
int p_1 = const_fn_variadic (p);
int p_2 = const_fn_variadic (p);
__analyzer_eval (x_1 == x_2); /* { dg-warning "TRUE" } */
__analyzer_eval (p_1 == p_2); /* { dg-warning "TRUE" } */
__analyzer_eval (x_1 == p_1); /* { dg-warning "UNKNOWN" } */
/* 1 extra arg, for 2 args in total. */
int xy_1 = const_fn_variadic (x, y);
int xy_2 = const_fn_variadic (x, y);
int pq_1 = const_fn_variadic (p, q);
int pq_2 = const_fn_variadic (p, q);
__analyzer_eval (xy_1 == xy_2); /* { dg-warning "TRUE" } */
__analyzer_eval (pq_1 == pq_2); /* { dg-warning "TRUE" } */
__analyzer_eval (xy_1 == pq_1); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (x_1 == xy_1); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (p_1 == pq_1); /* { dg-warning "UNKNOWN" } */
/* Above that, we don't track results. */
int xyz_1 = const_fn_variadic (x, y, z);
int xyz_2 = const_fn_variadic (x, y, z);
int pqr_1 = const_fn_variadic (p, q, r);
int pqr_2 = const_fn_variadic (p, q, r);
__analyzer_eval (xyz_1 == xyz_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (pqr_1 == pqr_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (xyz_1 == x_1); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (xyz_1 == xy_1); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (xyz_1 == pqr_1); /* { dg-warning "UNKNOWN" } */
}
/* Builtins with __attribute__ ((const)). */
void test_builtin_isascii (int x, int y)
{
int x_1 = __builtin_isascii (x);
int x_2 = __builtin_isascii (x);
int y_1 = __builtin_isascii (y);
int y_2 = __builtin_isascii (y);
__analyzer_eval (x_1 == x_2); /* { dg-warning "TRUE" } */
__analyzer_eval (y_1 == y_2); /* { dg-warning "TRUE" } */
__analyzer_eval (x_1 == y_1); /* { dg-warning "UNKNOWN" } */
}
void test_builtin_popcount (unsigned x, unsigned y)
{
unsigned x_1 = __builtin_popcount (x);
unsigned x_2 = __builtin_popcount (x);
unsigned y_1 = __builtin_popcount (y);
unsigned y_2 = __builtin_popcount (y);
__analyzer_eval (x_1 == x_2); /* { dg-warning "TRUE" } */
__analyzer_eval (y_1 == y_2); /* { dg-warning "TRUE" } */
__analyzer_eval (x_1 == y_1); /* { dg-warning "UNKNOWN" } */
}
void test_loop (void)
{
for (int i = 0; i < 100; i++)
{
int iter_val_a = const_fn_1 (i);
int iter_val_b = const_fn_1 (i);
__analyzer_eval (iter_val_a == iter_val_b); /* { dg-warning "TRUE" } */
}
}

View File

@ -0,0 +1,16 @@
extern int const_p (int) __attribute__((const));
extern void do_stuff (void);
void test (int a)
{
void *p;
if (const_p (a))
{
p = __builtin_malloc (1024);
if (!p)
return;
}
do_stuff ();
if (const_p (a))
__builtin_free (p); /* { dg-bogus "uninit" } */
}

View File

@ -0,0 +1,26 @@
/* Verify that we handle unknown values passed to __attribute__ ((const))
(by imposing a complexity limit). */
/* { dg-additional-options "--param analyzer-max-svalue-depth=0" } */
#include "analyzer-decls.h"
extern int const_fn_1 (int) __attribute__ ((const));
void test_const_fn_1 (int x, int y)
{
int x_1 = const_fn_1 (x);
int x_2 = const_fn_1 (x);
int y_1 = const_fn_1 (y);
int y_2 = const_fn_1 (y);
__analyzer_eval (x_1 == x_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (y_1 == y_2); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (x_1 == y_1); /* { dg-warning "UNKNOWN" } */
}
void test_2 (int x)
{
int once = const_fn_1 (x);
int again = const_fn_1 (once);
__analyzer_eval (once == again); /* { dg-warning "UNKNOWN" } */
}

View File

@ -0,0 +1,173 @@
/* { dg-additional-options "-Wno-analyzer-too-complex" } */
#include "pr104434.h"
/* Declare LAPACKE_lsame with __attribute__((const)). */
int LAPACKE_lsame( char ca, char cb ) __attribute__((const));
/* Testcase adapted/reduced from
https://github.com/xianyi/OpenBLAS/blob/c5f280a7f0e875d83833d895b2b8b0e341efabf4/lapack-netlib/LAPACKE/src/lapacke_cgbbrd_work.c
which has this license text. */
/*****************************************************************************
Copyright (c) 2014, Intel Corp.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************
* Contents: Native middle-level C interface to LAPACK function cgbbrd
* Author: Intel Corporation
* Generated November 2015
*****************************************************************************/
lapack_int LAPACKE_cgbbrd_work( int matrix_layout, char vect, lapack_int m,
lapack_int n, lapack_int ncc, lapack_int kl,
lapack_int ku, lapack_complex_float* ab,
lapack_int ldab, float* d, float* e,
lapack_complex_float* q, lapack_int ldq,
lapack_complex_float* pt, lapack_int ldpt,
lapack_complex_float* c, lapack_int ldc,
lapack_complex_float* work, float* rwork )
{
lapack_int info = 0;
if( matrix_layout == LAPACK_COL_MAJOR ) {
/* Call LAPACK function and adjust info */
LAPACK_cgbbrd( &vect, &m, &n, &ncc, &kl, &ku, ab, &ldab, d, e, q, &ldq,
pt, &ldpt, c, &ldc, work, rwork, &info );
if( info < 0 ) {
info = info - 1;
}
} else if( matrix_layout == LAPACK_ROW_MAJOR ) {
lapack_int ldab_t = MAX(1,kl+ku+1);
lapack_int ldc_t = MAX(1,m);
lapack_int ldpt_t = MAX(1,n);
lapack_int ldq_t = MAX(1,m);
lapack_complex_float* ab_t = NULL;
lapack_complex_float* q_t = NULL;
lapack_complex_float* pt_t = NULL;
lapack_complex_float* c_t = NULL;
/* Check leading dimension(s) */
if( ldab < n ) {
info = -9;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldc < ncc ) {
info = -17;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldpt < n ) {
info = -15;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldq < m ) {
info = -13;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
/* Allocate memory for temporary array(s) */
ab_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) * ldab_t * MAX(1,n) );
if( ab_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_0;
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
q_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldq_t * MAX(1,m) );
if( q_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_1;
}
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
pt_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldpt_t * MAX(1,n) );
if( pt_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_2;
}
}
if( ncc != 0 ) {
c_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldc_t * MAX(1,ncc) );
if( c_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_3;
}
}
/* Transpose input matrices */
LAPACKE_cgb_trans( matrix_layout, m, n, kl, ku, ab, ldab, ab_t, ldab_t );
if( ncc != 0 ) {
LAPACKE_cge_trans( matrix_layout, m, ncc, c, ldc, c_t, ldc_t );
}
/* Call LAPACK function and adjust info */
LAPACK_cgbbrd( &vect, &m, &n, &ncc, &kl, &ku, ab_t, &ldab_t, d, e, q_t,
&ldq_t, pt_t, &ldpt_t, c_t, &ldc_t, work, rwork, &info );
if( info < 0 ) {
info = info - 1;
}
/* Transpose output matrices */
LAPACKE_cgb_trans( LAPACK_COL_MAJOR, m, n, kl, ku, ab_t, ldab_t, ab,
ldab );
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, m, m, q_t, ldq_t, q, ldq );
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, n, n, pt_t, ldpt_t, pt, ldpt );
}
if( ncc != 0 ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, m, ncc, c_t, ldc_t, c, ldc );
}
/* Release memory and exit */
if( ncc != 0 ) {
LAPACKE_free( c_t );
}
exit_level_3:
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
LAPACKE_free( pt_t );
}
exit_level_2:
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
LAPACKE_free( q_t );
}
exit_level_1:
LAPACKE_free( ab_t );
exit_level_0:
if( info == LAPACK_TRANSPOSE_MEMORY_ERROR ) {
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
}
} else {
info = -1;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
}
return info; /* { dg-bogus "leak of 'q_t'" "leak of q_t" } */
/* { dg-bogus "leak of 'pt_t'" "leak of pt_t" { target *-*-* } .-1 } */
}

View File

@ -0,0 +1,173 @@
/* { dg-additional-options "-Wno-analyzer-too-complex" } */
#include "pr104434.h"
/* Declare LAPACKE_lsame *without* __attribute__((const)). */
int LAPACKE_lsame( char ca, char cb );
/* Testcase adapted/reduced from
https://github.com/xianyi/OpenBLAS/blob/c5f280a7f0e875d83833d895b2b8b0e341efabf4/lapack-netlib/LAPACKE/src/lapacke_cgbbrd_work.c
which has this license text. */
/*****************************************************************************
Copyright (c) 2014, Intel Corp.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************
* Contents: Native middle-level C interface to LAPACK function cgbbrd
* Author: Intel Corporation
* Generated November 2015
*****************************************************************************/
lapack_int LAPACKE_cgbbrd_work( int matrix_layout, char vect, lapack_int m,
lapack_int n, lapack_int ncc, lapack_int kl,
lapack_int ku, lapack_complex_float* ab,
lapack_int ldab, float* d, float* e,
lapack_complex_float* q, lapack_int ldq,
lapack_complex_float* pt, lapack_int ldpt,
lapack_complex_float* c, lapack_int ldc,
lapack_complex_float* work, float* rwork )
{
lapack_int info = 0;
if( matrix_layout == LAPACK_COL_MAJOR ) {
/* Call LAPACK function and adjust info */
LAPACK_cgbbrd( &vect, &m, &n, &ncc, &kl, &ku, ab, &ldab, d, e, q, &ldq,
pt, &ldpt, c, &ldc, work, rwork, &info );
if( info < 0 ) {
info = info - 1;
}
} else if( matrix_layout == LAPACK_ROW_MAJOR ) {
lapack_int ldab_t = MAX(1,kl+ku+1);
lapack_int ldc_t = MAX(1,m);
lapack_int ldpt_t = MAX(1,n);
lapack_int ldq_t = MAX(1,m);
lapack_complex_float* ab_t = NULL;
lapack_complex_float* q_t = NULL;
lapack_complex_float* pt_t = NULL;
lapack_complex_float* c_t = NULL;
/* Check leading dimension(s) */
if( ldab < n ) {
info = -9;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldc < ncc ) {
info = -17;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldpt < n ) {
info = -15;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
if( ldq < m ) {
info = -13;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
return info;
}
/* Allocate memory for temporary array(s) */
ab_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) * ldab_t * MAX(1,n) );
if( ab_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_0;
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
q_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldq_t * MAX(1,m) );
if( q_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_1;
}
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
pt_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldpt_t * MAX(1,n) );
if( pt_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_2;
}
}
if( ncc != 0 ) {
c_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldc_t * MAX(1,ncc) );
if( c_t == NULL ) {
info = LAPACK_TRANSPOSE_MEMORY_ERROR;
goto exit_level_3;
}
}
/* Transpose input matrices */
LAPACKE_cgb_trans( matrix_layout, m, n, kl, ku, ab, ldab, ab_t, ldab_t );
if( ncc != 0 ) {
LAPACKE_cge_trans( matrix_layout, m, ncc, c, ldc, c_t, ldc_t );
}
/* Call LAPACK function and adjust info */
LAPACK_cgbbrd( &vect, &m, &n, &ncc, &kl, &ku, ab_t, &ldab_t, d, e, q_t,
&ldq_t, pt_t, &ldpt_t, c_t, &ldc_t, work, rwork, &info );
if( info < 0 ) {
info = info - 1;
}
/* Transpose output matrices */
LAPACKE_cgb_trans( LAPACK_COL_MAJOR, m, n, kl, ku, ab_t, ldab_t, ab,
ldab );
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, m, m, q_t, ldq_t, q, ldq );
}
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, n, n, pt_t, ldpt_t, pt, ldpt );
}
if( ncc != 0 ) {
LAPACKE_cge_trans( LAPACK_COL_MAJOR, m, ncc, c_t, ldc_t, c, ldc );
}
/* Release memory and exit */
if( ncc != 0 ) {
LAPACKE_free( c_t );
}
exit_level_3:
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
LAPACKE_free( pt_t );
}
exit_level_2:
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'q' ) ) {
LAPACKE_free( q_t );
}
exit_level_1:
LAPACKE_free( ab_t );
exit_level_0:
if( info == LAPACK_TRANSPOSE_MEMORY_ERROR ) {
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
}
} else {
info = -1;
LAPACKE_xerbla( "LAPACKE_cgbbrd_work", info );
}
return info; /* { dg-warning "leak of 'q_t'" "leak of q_t" } */
/* { dg-warning "leak of 'pt_t'" "leak of pt_t" { target *-*-* } .-1 } */
}

View File

@ -0,0 +1,108 @@
/* Shared header for testing memory leak false positives seen in OpenBLAS,
e.g. in lapacke_cgbbrd_work.c.
The code is of the form:
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
pt_t = (lapack_complex_float*)
LAPACKE_malloc( sizeof(lapack_complex_float) *
ldpt_t * MAX(1,n) );
...snip...
}
[...snip lots of code...]
if( LAPACKE_lsame( vect, 'b' ) || LAPACKE_lsame( vect, 'p' ) ) {
LAPACKE_free( pt_t );
}
where LAPACKE_lsame is a case-insensitive comparison, implemented in its
own source file. Without __attribute__((const)) on LAPACKE_lsame, the
analyzer considers the execution paths where the malloc is called, and
then the free is not called. With __attribute__((const)), the analyzer
ought to rule out such paths. */
#define NULL ((void *)0)
typedef __SIZE_TYPE__ size_t;
extern void *malloc (size_t __size)
__attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__malloc__))
__attribute__ ((__alloc_size__ (1)))
__attribute__ ((__warn_unused_result__));
extern void free (void *__ptr)
__attribute__ ((__nothrow__ , __leaf__));
/* Header adapted/reduced from
https://github.com/xianyi/OpenBLAS/
which has this license text. */
/*****************************************************************************
Copyright (c) 2014, Intel Corp.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#define lapack_int int
#define lapack_logical lapack_int
#define LAPACKE_malloc( size ) malloc( size )
#define LAPACKE_free( p ) free( p )
#define LAPACK_ROW_MAJOR 101
#define LAPACK_COL_MAJOR 102
#define LAPACK_TRANSPOSE_MEMORY_ERROR -1011
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#define LAPACK_GLOBAL(lcname,UCNAME) lcname##_
typedef float _Complex lapack_complex_float;
void LAPACKE_xerbla( const char *name, int info );
void LAPACKE_cgb_trans( int matrix_layout, int m, int n,
int kl, int ku,
const lapack_complex_float *in, int ldin,
lapack_complex_float *out, int ldout );
void LAPACKE_cge_trans( int matrix_layout, int m, int n,
const lapack_complex_float* in, int ldin,
lapack_complex_float* out, int ldout );
#define LAPACK_cgbbrd LAPACK_GLOBAL(cgbbrd,CGBBRD)
void LAPACK_cgbbrd(
char const* vect,
lapack_int const* m, lapack_int const* n, lapack_int const* ncc, lapack_int const* kl, lapack_int const* ku,
lapack_complex_float* AB, lapack_int const* ldab,
float* D,
float* E,
lapack_complex_float* Q, lapack_int const* ldq,
lapack_complex_float* PT, lapack_int const* ldpt,
lapack_complex_float* C, lapack_int const* ldc,
lapack_complex_float* work,
float* rwork,
lapack_int* info );