From aee1adf2cdc1cf4e116e5c05b6e7c92b0fbb264b Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 23 Feb 2022 09:14:58 -0500 Subject: [PATCH] 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 ::test): New. (template <> struct default_hash_traits): 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 --- 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/testsuite/gcc.dg/analyzer/pr104434-const.c | 173 ++++++++++++++++++++++ gcc/testsuite/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 7e58bcd..36db4f2 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 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 95d9921..65daa34 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 917af22..bc8f554 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 &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 d4d7816..5cfa354 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 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 c2c89a2..aa489d0 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 &inputs); + const svalue * + get_or_create_const_fn_result_svalue (tree type, + tree fndecl, + const vec &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_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 c06ec9b..553edae 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 45cd0fc..8040712 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 { 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 &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 (1); } + void mark_empty () { m_fndecl = NULL; } + bool is_deleted () const + { + return m_fndecl == reinterpret_cast (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 &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 ::test (const svalue *sval) +{ + return sval->get_kind () == SK_CONST_FN_RESULT; +} + +template <> struct default_hash_traits +: public member_function_hash_traits +{ + 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 0000000..39e813f --- /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 0000000..ab79514 --- /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 0000000..2e6ccda --- /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 0000000..eacf3f6 --- /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 0000000..6bcb5bf --- /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 0000000..473cc67 --- /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.7.4