Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as 4892b3087412e6afc261cc9977ef4b54c799660f.
gcc/analyzer/ChangeLog: * store.cc (bit_range::intersects_p): New overload. (bit_range::operator-): New. (binding_cluster::maybe_get_compound_binding): Handle the partial overlap case. (selftest::test_bit_range_intersects_p): Add test coverage for new overload of bit_range::intersects_p. * store.h (bit_range::intersects_p): New overload. (bit_range::operator-): New. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/data-model-22.c: New test. * gcc.dg/analyzer/uninit-6.c: New test. * gcc.dg/analyzer/uninit-6b.c: New test. --- gcc/analyzer/store.cc | 77 ++++++++++++- gcc/analyzer/store.h | 5 + gcc/testsuite/gcc.dg/analyzer/data-model-22.c | 101 ++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/uninit-6.c | 29 +++++ gcc/testsuite/gcc.dg/analyzer/uninit-6b.c | 29 +++++ 5 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-22.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/uninit-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/uninit-6b.c diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index eac1295c5de..3760858c26d 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -253,6 +253,35 @@ bit_range::contains_p (const bit_range &other, bit_range *out) const return false; } +/* If OTHER intersects this, return true and write + the relative range of OTHER within THIS to *OUT_THIS, + and the relative range of THIS within OTHER to *OUT_OTHER. + Otherwise return false. */ + +bool +bit_range::intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const +{ + if (get_start_bit_offset () < other.get_next_bit_offset () + && other.get_start_bit_offset () < get_next_bit_offset ()) + { + bit_offset_t overlap_start + = MAX (get_start_bit_offset (), + other.get_start_bit_offset ()); + bit_offset_t overlap_next + = MIN (get_next_bit_offset (), + other.get_next_bit_offset ()); + gcc_assert (overlap_next > overlap_start); + bit_range abs_overlap_bits (overlap_start, overlap_next - overlap_start); + *out_this = abs_overlap_bits - get_start_bit_offset (); + *out_other = abs_overlap_bits - other.get_start_bit_offset (); + return true; + } + else + return false; +} + int bit_range::cmp (const bit_range &br1, const bit_range &br2) { @@ -263,6 +292,14 @@ bit_range::cmp (const bit_range &br1, const bit_range &br2) return wi::cmpu (br1.m_size_in_bits, br2.m_size_in_bits); } +/* Offset this range by OFFSET. */ + +bit_range +bit_range::operator- (bit_offset_t offset) const +{ + return bit_range (m_start_bit_offset - offset, m_size_in_bits); +} + /* If MASK is a contiguous range of set bits, write them to *OUT and return true. Otherwise return false. */ @@ -1570,9 +1607,29 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, } else { - /* REG and the bound range partially overlap. - We don't handle this case yet. */ - return NULL; + /* REG and the bound range partially overlap. */ + bit_range reg_subrange (0, 0); + bit_range bound_subrange (0, 0); + reg_range.intersects_p (bound_range, + ®_subrange, &bound_subrange); + + /* Get the bits from the bound value for the bits at the + intersection (relative to the bound value). */ + const svalue *overlap_sval + = sval->extract_bit_range (NULL_TREE, + bound_subrange, + mgr->get_svalue_manager ()); + + /* Get key for overlap, relative to the REG. */ + const concrete_binding *overlap_concrete_key + = mgr->get_concrete_binding (reg_subrange); + result_map.put (overlap_concrete_key, overlap_sval); + + /* Clobber default_map, removing/trimming/spliting where + it overlaps with overlap_concrete_key. */ + default_map.remove_overlapping_bindings (mgr, + overlap_concrete_key, + NULL); } } else @@ -2905,6 +2962,20 @@ test_bit_range_intersects_p () ASSERT_FALSE (b3_to_5.intersects_p (b6_to_7)); ASSERT_FALSE (b6_to_7.intersects_p (b3_to_5)); + + bit_range r1 (0,0); + bit_range r2 (0,0); + ASSERT_TRUE (b1_to_6.intersects_p (b0_to_7, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 0); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 1); + ASSERT_EQ (r2.m_size_in_bits, 6); + + ASSERT_TRUE (b0_to_7.intersects_p (b1_to_6, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 1); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 0); + ASSERT_EQ (r2.m_size_in_bits, 6); } /* Implementation detail of ASSERT_BIT_RANGE_FROM_MASK_EQ. */ diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index b75691e4a16..da82bd1bdec 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -269,9 +269,14 @@ struct bit_range return (get_start_bit_offset () < other.get_next_bit_offset () && other.get_start_bit_offset () < get_next_bit_offset ()); } + bool intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const; static int cmp (const bit_range &br1, const bit_range &br2); + bit_range operator- (bit_offset_t offset) const; + static bool from_mask (unsigned HOST_WIDE_INT mask, bit_range *out); bool as_byte_range (byte_range *out) const; diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-22.c b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c new file mode 100644 index 00000000000..8429b2f4dc6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c @@ -0,0 +1,101 @@ +#include <string.h> +#include "analyzer-decls.h" + +extern void check_init_char (char v); +extern void check_init_int (int v); + +void test_1 (void) +{ + union + { + char c[16]; + int i[4]; + } v; + memset (&v, 0, sizeof (v)); + v.c[5] = 42; + check_init_int (v.c[0]); + check_init_int (v.c[4]); + check_init_int (v.c[6]); + check_init_int (v.i[1]); +} + +void test_2 (void) +{ + /* Intersection of byte ranges within "v". */ + union + { + struct { + int a; + char b; + char c; + } __attribute__((packed)) icc; + struct { + char a; + int b; + char c; + } __attribute__((packed)) cic; + struct { + char a; + char b; + int c; + } __attribute__((packed)) cci; + } v; + + v.icc.a = 1066; + v.icc.b = 42; + v.icc.c = 17; + + __analyzer_eval (v.icc.a == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.b == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cic.a = 42; + v.cic.b = 1066; + v.cic.c = 17; + + __analyzer_eval (v.cic.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.b == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cci.a = 42; + v.cci.b = 17; + v.cci.c = 1066; + + __analyzer_eval (v.cci.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.b == 17); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.c == 1066); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c new file mode 100644 index 00000000000..75a99ad2c44 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + struct ethtool_link_settings base; + u32 lanes; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c new file mode 100644 index 00000000000..32ba30fb384 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + u32 lanes; + struct ethtool_link_settings base; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} -- 2.26.3