[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