[committed] analyzer: handle __attribute__((const)) [PR104434]
David Malcolm
dmalcolm@redhat.com
Wed Feb 23 23:54:10 GMT 2022
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.
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r12-7364-gaee1adf2cdc1cf4e116e5c05b6e7c92b0fbb264b.
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>
---
gcc/analyzer/analyzer.h | 1 +
gcc/analyzer/region-model-impl-calls.cc | 8 +
gcc/analyzer/region-model-manager.cc | 28 +++
gcc/analyzer/region-model.cc | 59 +++++-
gcc/analyzer/region-model.h | 10 +
gcc/analyzer/svalue.cc | 73 ++++++++
gcc/analyzer/svalue.h | 133 +++++++++++++-
gcc/testsuite/gcc.dg/analyzer/attr-const-1.c | 152 +++++++++++++++
gcc/testsuite/gcc.dg/analyzer/attr-const-2.c | 16 ++
gcc/testsuite/gcc.dg/analyzer/attr-const-3.c | 26 +++
.../gcc.dg/analyzer/pr104434-const.c | 173 ++++++++++++++++++
.../gcc.dg/analyzer/pr104434-nonconst.c | 173 ++++++++++++++++++
gcc/testsuite/gcc.dg/analyzer/pr104434.h | 108 +++++++++++
13 files changed, 954 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/analyzer/attr-const-1.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/attr-const-2.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/attr-const-3.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr104434-const.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr104434-nonconst.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr104434.h
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 7e58bcd5d70..36db4f2b538 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -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;
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 95d9921c61d..65daa342824 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -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 *
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index 917af22c4f5..bc8f554580b 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -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);
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index d4d7816e0d5..5cfa3543f17 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -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);
}
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index c2c89a20d50..aa489d06a38 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -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; }
diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
index c06ec9b6dc3..553edae7250 100644
--- a/gcc/analyzer/svalue.cc
+++ b/gcc/analyzer/svalue.cc
@@ -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 */
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
index 45cd0fc3dda..8040712ec2e 100644
--- a/gcc/analyzer/svalue.h
+++ b/gcc/analyzer/svalue.h
@@ -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 */
diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-const-1.c b/gcc/testsuite/gcc.dg/analyzer/attr-const-1.c
new file mode 100644
index 00000000000..39e813f14c6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/attr-const-1.c
@@ -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" } */
+ }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-const-2.c b/gcc/testsuite/gcc.dg/analyzer/attr-const-2.c
new file mode 100644
index 00000000000..ab79514acf8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/attr-const-2.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-const-3.c b/gcc/testsuite/gcc.dg/analyzer/attr-const-3.c
new file mode 100644
index 00000000000..2e6ccda74dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/attr-const-3.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104434-const.c b/gcc/testsuite/gcc.dg/analyzer/pr104434-const.c
new file mode 100644
index 00000000000..eacf3f6cfd7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104434-const.c
@@ -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 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104434-nonconst.c b/gcc/testsuite/gcc.dg/analyzer/pr104434-nonconst.c
new file mode 100644
index 00000000000..6bcb5bfc814
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104434-nonconst.c
@@ -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 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104434.h b/gcc/testsuite/gcc.dg/analyzer/pr104434.h
new file mode 100644
index 00000000000..473cc673f43
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104434.h
@@ -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 );
--
2.26.3
More information about the Gcc-patches
mailing list