From 2ae94a50d86b531e1e0a5497a972d5d7d497b800 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 1 Mar 2021 16:28:10 -0500
Subject: [PATCH 20/20] FIXME: WIP on bitfield support (on x86_64)

---
 gcc/analyzer/region-model-manager.cc | 82 +++++++++++++++++++++++++-
 gcc/analyzer/region-model.cc         | 13 ++++-
 gcc/analyzer/region-model.h          |  3 +
 gcc/analyzer/region.cc               | 48 +++++++++++++++
 gcc/analyzer/region.h                | 87 ++++++++++++++++++++++++++++
 5 files changed, 228 insertions(+), 5 deletions(-)

diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index 2de1c46023a..38c545019ca 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -431,6 +431,43 @@ region_model_manager::get_or_create_cast (tree type, const svalue *arg)
   return get_or_create_unaryop (type, op, arg);
 }
 
+/* Return true if MASK is the mask for a contiguous run of bits, writing
+   the bit position of the start of the run to *OUT_LOW and the
+   number to *OUT_NUM.
+   Return false otherwise.  */
+
+static bool
+mask_for_bit_range_p (unsigned mask, unsigned *out_low, unsigned *out_num)
+{
+  /* Ensure the loop to find lowest_set_bit terminates.  */
+  if (!mask)
+    return false;
+
+  /* Find lowest set bit.  */
+  unsigned lowest_set_bit = 0;
+  while ((mask & 1) == 0)
+    {
+      lowest_set_bit++;
+      mask >>= 1;
+    }
+
+  /* Find number of contiguously set bits, bailing out if there is a break
+     in the set bits.  */
+  unsigned num_set_bits = 1;
+  while (mask >>= 1)
+    {
+      if ((mask & 1) == 0)
+	return false;
+      num_set_bits++;
+    }
+
+  /* Success.  */
+  *out_low = lowest_set_bit;
+  *out_num = num_set_bits;
+  return true;
+  // TODO: selftests!
+}
+
 /* Subroutine of region_model_manager::get_or_create_binop.
    Attempt to fold the inputs and return a simpler svalue *.
    Otherwise, return NULL.  */
@@ -480,9 +517,35 @@ region_model_manager::maybe_fold_binop (tree type, enum tree_code op,
       break;
     case BIT_AND_EXPR:
       if (cst1)
-	if (zerop (cst1) && INTEGRAL_TYPE_P (type))
-	  /* "(ARG0 & 0)" -> "0".  */
-	  return get_or_create_constant_svalue (build_int_cst (type, 0));
+	{
+	  if (zerop (cst1) && INTEGRAL_TYPE_P (type))
+	    /* "(ARG0 & 0)" -> "0".  */
+	    return get_or_create_constant_svalue (build_int_cst (type, 0));
+
+	  /* (COMPOUND & CST) can arise when extracting bit fields.  */
+	  if (const compound_svalue *compound_sval0
+		= arg0->dyn_cast_compound_svalue ())
+	    {
+	      const binding_map &map = compound_sval0->get_map ();
+	      unsigned mask = tree_to_uhwi (cst1);
+	      unsigned low, num;
+	      if (mask_for_bit_range_p (mask, &low, &num))
+		{
+		  /* Does map have a binding for this specific run of bits?  */
+		  const concrete_binding *key
+		    = m_store_mgr.get_concrete_binding (low, num, BK_direct);
+		  if (const svalue *sval = map.get (key))
+		    {
+		      /* Left shift by correct number of bits.  */
+		      return get_or_create_binop
+			(type, LSHIFT_EXPR,
+			 get_or_create_cast (type, sval),
+			 get_or_create_constant_svalue (build_int_cst (type, // FIXME
+								       low)));
+		    }
+		}
+	    }
+	}
       break;
     case TRUTH_ANDIF_EXPR:
     case TRUTH_AND_EXPR:
@@ -892,6 +955,19 @@ region_model_manager::get_offset_region (const region *parent,
   return offset_reg;
 }
 
+/* FIXME.  */
+
+const region *
+region_model_manager::get_bit_field_region (const region *parent,
+					    unsigned num_bits,
+					    unsigned pos)
+{
+  // FIXME: type
+  // FIXME: reuse
+  return new bit_field_region (alloc_region_id (), parent, NULL_TREE,
+			       num_bits, pos);
+}
+
 /* Return the region that describes accessing PARENT_REGION as if
    it were of type TYPE, creating it if necessary.  */
 
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 5848319a403..5b444d3338a 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1224,6 +1224,17 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
       }
       break;
 
+    case BIT_FIELD_REF:
+      {
+	tree arg0 = TREE_OPERAND (expr, 0);
+	unsigned int num_bits = tree_to_uhwi (TREE_OPERAND (expr, 1));
+	unsigned int pos = tree_to_uhwi (TREE_OPERAND (expr, 2));
+	const region *parent
+	  = get_lvalue (path_var (arg0, pv.m_stack_depth), ctxt);
+	return m_mgr->get_bit_field_region (parent, num_bits, pos);
+      }
+      break;
+
     case MEM_REF:
       {
 	tree ptr = TREE_OPERAND (expr, 0);
@@ -1343,8 +1354,6 @@ region_model::get_rvalue_1 (path_var pv, region_model_context *ctxt)
       break;
 
     case BIT_FIELD_REF:
-      return m_mgr->get_or_create_unknown_svalue (TREE_TYPE (pv.m_tree));
-
     case SSA_NAME:
     case VAR_DECL:
     case PARM_DECL:
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 8e7b36a6d08..b638ca34587 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -238,6 +238,9 @@ public:
   const region *get_offset_region (const region *parent,
 				   tree type,
 				   const svalue *byte_offset);
+  const region *get_bit_field_region (const region *parent,
+				      unsigned num_bits,
+				      unsigned pos);
   const region *get_cast_region (const region *original_region,
 				 tree type);
   const frame_region *get_frame_region (const frame_region *calling_frame,
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index 6db1fc91afd..424c57e2948 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -98,6 +98,7 @@ region::get_base_region () const
 	case RK_FIELD:
 	case RK_ELEMENT:
 	case RK_OFFSET:
+	case RK_BIT_FIELD:
 	  iter = iter->get_parent_region ();
 	  continue;
 	case RK_CAST:
@@ -121,6 +122,7 @@ region::base_region_p () const
     case RK_FIELD:
     case RK_ELEMENT:
     case RK_OFFSET:
+    case RK_BIT_FIELD:
     case RK_CAST:
       return false;
 
@@ -215,6 +217,18 @@ region::get_byte_size (byte_size_t *out) const
 bool
 region::get_bit_size (bit_size_t *out) const
 {
+  tree type = get_type ();
+
+  /* Bail out e.g. for heap-allocated regions.  */
+  if (!type)
+    return false;
+
+  if (TREE_CODE (type) == INTEGER_TYPE)
+    {
+      *out = TYPE_PRECISION (type);
+      return true;
+    }
+
   byte_size_t byte_size;
   if (!get_byte_size (&byte_size))
     return false;
@@ -418,6 +432,15 @@ region::calc_offset () const
 	  }
 	  continue;
 
+	case RK_BIT_FIELD:
+	  {
+	    const bit_field_region *offset_reg
+	      = (const bit_field_region *)iter_region;
+	    iter_region = iter_region->get_parent_region ();
+	    accum_bit_offset += offset_reg->get_position ();
+	  }
+	  continue;
+
 	case RK_CAST:
 	  {
 	    const cast_region *cast_reg
@@ -1078,6 +1101,31 @@ offset_region::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
+/* class bit_field_region : public region.  */
+
+/* FIXME.  */
+
+void
+bit_field_region::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "BIT_FIELD_REG(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_printf (pp, ", %i, %i)", m_num_bits, m_pos);
+    }
+  else
+    {
+      pp_string (pp, "bit_field_region(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_printf (pp, ", %i, %i)", m_num_bits, m_pos);
+    }
+}
+
 /* class cast_region : public region.  */
 
 /* Implementation of region::accept vfunc for cast_region.  */
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index ea24b38b6a1..67842f01311 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -43,6 +43,7 @@ enum region_kind
   RK_FIELD,
   RK_ELEMENT,
   RK_OFFSET,
+  RK_BIT_FIELD,
   RK_CAST,
   RK_HEAP_ALLOCATED,
   RK_ALLOCA,
@@ -70,6 +71,7 @@ enum region_kind
      field_region (RK_FIELD)
      element_region (RK_ELEMENT)
      offset_region (RK_OFFSET)
+     bit_field_region (RK_BIT_FIELD)
      cast_region (RK_CAST)
      heap_allocated_region (RK_HEAP_ALLOCATED)
      alloca_region (RK_ALLOCA)
@@ -860,6 +862,91 @@ template <> struct default_hash_traits<offset_region::key_t>
 
 namespace ana {
 
+/* Concrete region subclass representing the memory occupied by a
+   bit_field within a struct or union.  */
+
+class bit_field_region : public region
+{
+public:
+  /* A support class for uniquifying instances of bit_field_region.  */
+#if 0
+  struct key_t
+  {
+    key_t (const region *parent, tree bit_field)
+    : m_parent (parent), m_bit_field (bit_field)
+    {
+      gcc_assert (bit_field);
+    }
+
+    hashval_t hash () const
+    {
+      inchash::hash hstate;
+      hstate.add_ptr (m_parent);
+      // TODO
+#if 0
+      hstate.add_ptr (m_bit_field);
+#endif
+      return hstate.end ();
+    }
+
+    bool operator== (const key_t &other) const
+    {
+      return (m_parent == other.m_parent && m_bit_field == other.m_bit_field);
+    }
+
+    void mark_deleted () { m_bit_field = reinterpret_cast<tree> (1); }
+    void mark_empty () { m_bit_field = NULL_TREE; }
+    bool is_deleted () const { return m_bit_field == reinterpret_cast<tree> (1); }
+    bool is_empty () const { return m_bit_field == NULL_TREE; }
+
+    const region *m_parent;
+    tree m_bit_field;
+  };
+#endif
+
+  bit_field_region (unsigned id, const region *parent, tree type,
+		    unsigned num_bits, unsigned pos)
+  : region (complexity (parent), id, parent, type),
+    m_num_bits (num_bits), m_pos (pos)
+  {}
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_BIT_FIELD; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+#if 0
+  const bit_field_region *
+  dyn_cast_bit_field_region () const FINAL OVERRIDE { return this; }
+
+  tree get_bit_field () const { return m_bit_field; }
+#endif
+
+  unsigned get_position () const { return m_pos; }
+
+private:
+  unsigned m_num_bits;
+  unsigned m_pos;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const bit_field_region *>::test (const region *reg)
+{
+  return reg->get_kind () == RK_BIT_FIELD;
+}
+
+#if 0
+template <> struct default_hash_traits<bit_field_region::key_t>
+: public member_function_hash_traits<bit_field_region::key_t>
+{
+  static const bool empty_zero_p = true;
+};
+#endif
+
+namespace ana {
+
 /* A region that views another region using a different type.  */
 
 class cast_region : public region
-- 
2.26.2

