From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2209) id 899563858014; Wed, 2 Feb 2022 14:54:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 899563858014 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: David Malcolm To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-6998] analyzer: implement bit_range_region X-Act-Checkin: gcc X-Git-Author: David Malcolm X-Git-Refname: refs/heads/master X-Git-Oldrev: 9b4eee5fd158c4ee75d1f1000debbf5082fb9b56 X-Git-Newrev: 93e759fc18a1a4208ae2898610c55ebd8c9e25d8 Message-Id: <20220202145413.899563858014@sourceware.org> Date: Wed, 2 Feb 2022 14:54:13 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 02 Feb 2022 14:54:13 -0000 https://gcc.gnu.org/g:93e759fc18a1a4208ae2898610c55ebd8c9e25d8 commit r12-6998-g93e759fc18a1a4208ae2898610c55ebd8c9e25d8 Author: David Malcolm Date: Fri Jan 28 16:15:44 2022 -0500 analyzer: implement bit_range_region GCC 12 has gained -Wanalyzer-use-of-uninitialized-value, and I'm seeing various false positives from it due to region_model::get_lvalue not properly handling BIT_FIELD_REF, and falling back to using an UNKNOWN_REGION for them. This patch fixes these false positives by implementing a new bit_range_region region subclass for handling BIT_FIELD_REF. gcc/analyzer/ChangeLog: * analyzer.h (class bit_range_region): New forward decl. * region-model-manager.cc (region_model_manager::get_bit_range): New. (region_model_manager::log_stats): Handle m_bit_range_regions. * region-model.cc (region_model::get_lvalue_1): Handle BIT_FIELD_REF. * region-model.h (region_model_manager::get_bit_range): New decl. (region_model_manager::m_bit_range_regions): New field. * region.cc (region::get_base_region): Handle RK_BIT_RANGE. (region::base_region_p): Likewise. (region::calc_offset): Likewise. (bit_range_region::dump_to_pp): New. (bit_range_region::get_byte_size): New. (bit_range_region::get_bit_size): New. (bit_range_region::get_byte_size_sval): New. (bit_range_region::get_relative_concrete_offset): New. * region.h (enum region_kind): Add RK_BIT_RANGE. (region::dyn_cast_bit_range_region): New vfunc. (class bit_range_region): New. (is_a_helper ::test): New. (default_hash_traits): New. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/torture/uninit-bit-field-ref.c: New test. Signed-off-by: David Malcolm Diff: --- gcc/analyzer/analyzer.h | 1 + gcc/analyzer/region-model-manager.cc | 20 +++++ gcc/analyzer/region-model.cc | 14 ++++ gcc/analyzer/region-model.h | 4 + gcc/analyzer/region.cc | 84 ++++++++++++++++++++ gcc/analyzer/region.h | 89 ++++++++++++++++++++++ .../gcc.dg/analyzer/torture/uninit-bit-field-ref.c | 31 ++++++++ 7 files changed, 243 insertions(+) diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index c5bca2dec64..7e58bcd5d70 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -67,6 +67,7 @@ class region; class cast_region; class field_region; class string_region; + class bit_range_region; class region_model_manager; struct model_merger; class store_manager; diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index ba835cba22c..010ad078849 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -1494,6 +1494,25 @@ region_model_manager::get_region_for_string (tree string_cst) return reg; } +/* Return the region that describes accessing BITS within PARENT as TYPE, + creating it if necessary. */ + +const region * +region_model_manager::get_bit_range (const region *parent, tree type, + const bit_range &bits) +{ + gcc_assert (parent); + + bit_range_region::key_t key (parent, type, bits); + if (bit_range_region *reg = m_bit_range_regions.get (key)) + return reg; + + bit_range_region *bit_range_reg + = new bit_range_region (alloc_region_id (), parent, type, bits); + m_bit_range_regions.put (key, bit_range_reg); + return bit_range_reg; +} + /* If we see a tree code we don't know how to handle, rather than ICE or generate bogus results, create a dummy region, and notify CTXT so that it can mark the new state as being not properly @@ -1663,6 +1682,7 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const log_uniq_map (logger, show_objs, "frame_region", m_frame_regions); log_uniq_map (logger, show_objs, "symbolic_region", m_symbolic_regions); log_uniq_map (logger, show_objs, "string_region", m_string_map); + log_uniq_map (logger, show_objs, "bit_range_region", m_bit_range_regions); logger->log (" # managed dynamic regions: %i", m_managed_dynamic_regions.length ()); m_store_mgr.log_stats (logger, show_objs); diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 4c312b053f8..58c7028fc9c 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1707,6 +1707,20 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt) const } break; + case BIT_FIELD_REF: + { + tree inner_expr = TREE_OPERAND (expr, 0); + const region *inner_reg = get_lvalue (inner_expr, ctxt); + tree num_bits = TREE_OPERAND (expr, 1); + tree first_bit_offset = TREE_OPERAND (expr, 2); + gcc_assert (TREE_CODE (num_bits) == INTEGER_CST); + gcc_assert (TREE_CODE (first_bit_offset) == INTEGER_CST); + bit_range bits (TREE_INT_CST_LOW (first_bit_offset), + TREE_INT_CST_LOW (num_bits)); + return m_mgr->get_bit_range (inner_reg, TREE_TYPE (expr), bits); + } + break; + case MEM_REF: { tree ptr = TREE_OPERAND (expr, 0); diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 983d082ab3e..3fa090d771e 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -318,6 +318,8 @@ public: function *fun); const region *get_symbolic_region (const svalue *sval); const string_region *get_region_for_string (tree string_cst); + const region *get_bit_range (const region *parent, tree type, + const bit_range &bits); const region * get_region_for_unexpected_tree_code (region_model_context *ctxt, @@ -471,6 +473,8 @@ private: typedef hash_map string_map_t; string_map_t m_string_map; + consolidation_map m_bit_range_regions; + store_manager m_store_mgr; bounded_ranges_manager *m_range_mgr; diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index f5a2a0ba3df..9d8fdb22271 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -99,6 +99,7 @@ region::get_base_region () const case RK_ELEMENT: case RK_OFFSET: case RK_SIZED: + case RK_BIT_RANGE: iter = iter->get_parent_region (); continue; case RK_CAST: @@ -124,6 +125,7 @@ region::base_region_p () const case RK_OFFSET: case RK_SIZED: case RK_CAST: + case RK_BIT_RANGE: return false; default: @@ -547,6 +549,19 @@ region::calc_offset () const } continue; + case RK_BIT_RANGE: + { + const bit_range_region *bit_range_reg + = (const bit_range_region *)iter_region; + iter_region = iter_region->get_parent_region (); + + bit_offset_t rel_bit_offset; + if (!bit_range_reg->get_relative_concrete_offset (&rel_bit_offset)) + return region_offset::make_symbolic (iter_region); + accum_bit_offset += rel_bit_offset; + } + continue; + default: return region_offset::make_concrete (iter_region, accum_bit_offset); } @@ -1446,6 +1461,75 @@ string_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* class bit_range_region : public region. */ + +/* Implementation of region::dump_to_pp vfunc for bit_range_region. */ + +void +bit_range_region::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + { + pp_string (pp, "BIT_RANGE_REG("); + get_parent_region ()->dump_to_pp (pp, simple); + pp_string (pp, ", "); + m_bits.dump_to_pp (pp); + pp_string (pp, ")"); + } + else + { + pp_string (pp, "bit_range_region("); + get_parent_region ()->dump_to_pp (pp, simple); + pp_string (pp, ", "); + m_bits.dump_to_pp (pp); + pp_printf (pp, ")"); + } +} + +/* Implementation of region::get_byte_size vfunc for bit_range_region. */ + +bool +bit_range_region::get_byte_size (byte_size_t *out) const +{ + if (m_bits.m_size_in_bits % BITS_PER_UNIT == 0) + { + *out = m_bits.m_size_in_bits / BITS_PER_UNIT; + return true; + } + return false; +} + +/* Implementation of region::get_bit_size vfunc for bit_range_region. */ + +bool +bit_range_region::get_bit_size (bit_size_t *out) const +{ + *out = m_bits.m_size_in_bits; + return true; +} + +/* Implementation of region::get_byte_size_sval vfunc for bit_range_region. */ + +const svalue * +bit_range_region::get_byte_size_sval (region_model_manager *mgr) const +{ + if (m_bits.m_size_in_bits % BITS_PER_UNIT != 0) + return mgr->get_or_create_unknown_svalue (size_type_node); + + HOST_WIDE_INT num_bytes = m_bits.m_size_in_bits.to_shwi () / BITS_PER_UNIT; + return mgr->get_or_create_int_cst (size_type_node, num_bytes); +} + +/* Implementation of region::get_relative_concrete_offset vfunc for + bit_range_region. */ + +bool +bit_range_region::get_relative_concrete_offset (bit_offset_t *out) const +{ + *out = m_bits.get_start_bit_offset (); + return true; +} + /* class unknown_region : public region. */ /* Implementation of region::dump_to_pp vfunc for unknown_region. */ diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index 20eca524c04..206b157e908 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -60,6 +60,7 @@ enum region_kind RK_HEAP_ALLOCATED, RK_ALLOCA, RK_STRING, + RK_BIT_RANGE, RK_UNKNOWN }; @@ -88,6 +89,7 @@ enum region_kind heap_allocated_region (RK_HEAP_ALLOCATED) alloca_region (RK_ALLOCA) string_region (RK_STRING) + bit_range_region (RK_BIT_RANGE) unknown_region (RK_UNKNOWN). */ /* Abstract base class for representing ways of accessing chunks of memory. @@ -127,6 +129,8 @@ public: dyn_cast_cast_region () const { return NULL; } virtual const string_region * dyn_cast_string_region () const { return NULL; } + virtual const bit_range_region * + dyn_cast_bit_range_region () const { return NULL; } virtual void accept (visitor *v) const; @@ -1133,6 +1137,91 @@ is_a_helper ::test (const region *reg) namespace ana { +/* A region for a specific range of bits within another region. */ + +class bit_range_region : public region +{ +public: + /* A support class for uniquifying instances of bit_range_region. */ + struct key_t + { + key_t (const region *parent, tree type, const bit_range &bits) + : m_parent (parent), m_type (type), m_bits (bits) + { + gcc_assert (parent); + } + + hashval_t hash () const + { + inchash::hash hstate; + hstate.add_ptr (m_parent); + hstate.add_ptr (m_type); + hstate.add (&m_bits, sizeof (m_bits)); + return hstate.end (); + } + + bool operator== (const key_t &other) const + { + return (m_parent == other.m_parent + && m_type == other.m_type + && m_bits == other.m_bits); + } + + void mark_deleted () { m_parent = reinterpret_cast (1); } + void mark_empty () { m_parent = NULL; } + bool is_deleted () const + { + return m_parent == reinterpret_cast (1); + } + bool is_empty () const { return m_parent == NULL; } + + const region *m_parent; + tree m_type; + bit_range m_bits; + }; + + bit_range_region (unsigned id, const region *parent, tree type, + const bit_range &bits) + : region (complexity (parent), id, parent, type), + m_bits (bits) + {} + + const bit_range_region * + dyn_cast_bit_range_region () const FINAL OVERRIDE { return this; } + + enum region_kind get_kind () const FINAL OVERRIDE { return RK_BIT_RANGE; } + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + + const bit_range &get_bits () const { return m_bits; } + + bool get_byte_size (byte_size_t *out) const FINAL OVERRIDE; + bool get_bit_size (bit_size_t *out) const FINAL OVERRIDE; + const svalue *get_byte_size_sval (region_model_manager *mgr) const FINAL OVERRIDE; + bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; + +private: + bit_range m_bits; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper ::test (const region *reg) +{ + return reg->get_kind () == RK_BIT_RANGE; +} + +template <> struct default_hash_traits +: public member_function_hash_traits +{ + static const bool empty_zero_p = true; +}; + +namespace ana { + /* An unknown region, for handling unimplemented tree codes. */ class unknown_region : public region diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/uninit-bit-field-ref.c b/gcc/testsuite/gcc.dg/analyzer/torture/uninit-bit-field-ref.c new file mode 100644 index 00000000000..e2f91380a71 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/uninit-bit-field-ref.c @@ -0,0 +1,31 @@ +/* { dg-require-effective-target vect_int } */ +/* { dg-additional-options "-Wno-psabi" } */ + +typedef __INT32_TYPE__ int32_t; +typedef int32_t vnx4si __attribute__((vector_size (32))); + +extern void check_for_uninit (vnx4si v); + +void test_1a (vnx4si *out, int a, int b) +{ + vnx4si v = (vnx4si) { 1, 2, 3, 4, 5, 6, a, b }; + check_for_uninit (v); +} + +void test_1b (vnx4si *out, int a, int b) +{ + check_for_uninit ((vnx4si) { 1, 2, 3, 4, 5, 6, a, b }); +} + +static __attribute__((noipa)) void +called_by_test_2 (vnx4si *out, int a, int b) +{ + *out = (vnx4si) { 1, 2, 3, 4, 5, 6, a, b }; +} + +void test_2 (vnx4si *out, int a, int b) +{ + vnx4si v; + called_by_test_2 (&v, a, b); + check_for_uninit (v); +}