This patch introduces a template class ana::integer_with_unit<Unit>
wrapping the arbitrary-precision ana::integer introduced in the previous
patch, capturing the units of the integer in the type system: bits vs
bytes vs unitless.  The idea is that if we mess up bits vs bytes in the
analyzer implementation, we get a compile-time type error.  The class
also supports user-defined literals, so we can write e.g.
  8_bits
in the code, rather than
  integer_with_unit<units::bit> (8)

As in the previous patch, nothing uses this yet, but it is fundamental
to my upcoming rewrite of the analyzer's state-tracking.

gcc/ChangeLog:
        * Makefile.in (ANALYZER_OBJS): Add analyzer/integer-with-unit.o.

gcc/analyzer/ChangeLog:
        * analyzer-selftests.cc (run_analyzer_selftests): Call
        analyzer_integer_with_unit_cc_tests.
        * analyzer-selftests.h (analyzer_integer_with_unit_cc_tests): New
        decl.
        * integer-with-unit.cc: New file.
        * integer-with-unit.h: New file.
        * units.h: New file.

Signed-off-by: David Malcolm <[email protected]>
---
 gcc/Makefile.in                    |   1 +
 gcc/analyzer/analyzer-selftests.cc |   1 +
 gcc/analyzer/analyzer-selftests.h  |   1 +
 gcc/analyzer/integer-with-unit.cc  | 580 +++++++++++++++++++++++++++++
 gcc/analyzer/integer-with-unit.h   | 403 ++++++++++++++++++++
 gcc/analyzer/units.h               |  69 ++++
 6 files changed, 1055 insertions(+)
 create mode 100644 gcc/analyzer/integer-with-unit.cc
 create mode 100644 gcc/analyzer/integer-with-unit.h
 create mode 100644 gcc/analyzer/units.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5bb019c2343..7f95cbb7cdf 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1371,6 +1371,7 @@ ANALYZER_OBJS = \
        analyzer/infinite-loop.o \
        analyzer/infinite-recursion.o \
        analyzer/integer.o \
+       analyzer/integer-with-unit.o \
        analyzer/kf.o \
        analyzer/kf-analyzer.o \
        analyzer/kf-lang-cp.o \
diff --git a/gcc/analyzer/analyzer-selftests.cc 
b/gcc/analyzer/analyzer-selftests.cc
index cf5af41c8d7..1cfb2007a63 100644
--- a/gcc/analyzer/analyzer-selftests.cc
+++ b/gcc/analyzer/analyzer-selftests.cc
@@ -52,6 +52,7 @@ run_analyzer_selftests ()
   analyzer_constraint_manager_cc_tests ();
   analyzer_function_set_cc_tests ();
   analyzer_integer_cc_tests ();
+  analyzer_integer_with_unit_cc_tests ();
   analyzer_program_point_cc_tests ();
   analyzer_program_state_cc_tests ();
   analyzer_ranges_cc_tests ();
diff --git a/gcc/analyzer/analyzer-selftests.h 
b/gcc/analyzer/analyzer-selftests.h
index f3af1115f07..f32846fdcf8 100644
--- a/gcc/analyzer/analyzer-selftests.h
+++ b/gcc/analyzer/analyzer-selftests.h
@@ -37,6 +37,7 @@ extern void analyzer_access_diagram_cc_tests ();
 extern void analyzer_constraint_manager_cc_tests ();
 extern void analyzer_function_set_cc_tests ();
 extern void analyzer_integer_cc_tests ();
+extern void analyzer_integer_with_unit_cc_tests ();
 extern void analyzer_program_point_cc_tests ();
 extern void analyzer_program_state_cc_tests ();
 extern void analyzer_ranges_cc_tests ();
diff --git a/gcc/analyzer/integer-with-unit.cc 
b/gcc/analyzer/integer-with-unit.cc
new file mode 100644
index 00000000000..a10b0b940d7
--- /dev/null
+++ b/gcc/analyzer/integer-with-unit.cc
@@ -0,0 +1,580 @@
+/* Unit-safe arbitrary precision integers (e.g. "32 bytes").
+   Copyright (C) 2019-2026 Free Software Foundation, Inc.
+   Contributed by David Malcolm <[email protected]>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "analyzer/common.h"
+
+#include "selftest.h"
+
+#include "diagnostics/markup.h"
+
+#include "analyzer/integer-with-unit.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+template<>
+void
+integer_with_unit<units::byte>::dump_to_pp (pretty_printer *pp,
+                                           units::role role) const
+{
+  switch (role)
+    {
+    default:
+      gcc_unreachable ();
+    case units::role::generic:
+      m_integer.dump_to_pp (pp);
+      break;
+    case units::role::offset:
+      pp_string (pp, "byte ");
+      m_integer.dump_to_pp (pp);
+      break;
+    case units::role::size:
+      if (m_integer == 1)
+       pp_printf (pp, "1 byte");
+      else
+       {
+         m_integer.dump_to_pp (pp);
+         pp_string (pp, " bytes");
+       }
+      break;
+    }
+}
+
+template<>
+void
+integer_with_unit<units::bit>::dump_to_pp (pretty_printer *pp,
+                                          units::role role) const
+{
+  if (role != units::role::generic)
+    {
+      // Attempt to print as bytes
+      concrete_byte_size bytes;
+      if (maybe_bytes_from_bits (*this, &bytes))
+       {
+         bytes.dump_to_pp (pp, role);
+         return;
+       }
+    }
+  switch (role)
+    {
+    default:
+      gcc_unreachable ();
+    case units::role::generic:
+      m_integer.dump_to_pp (pp);
+      break;
+    case units::role::offset:
+      pp_string (pp, "bit ");
+      m_integer.dump_to_pp (pp);
+      break;
+    case units::role::size:
+      if (m_integer == 1)
+       pp_printf (pp, "1 bit");
+      else
+       {
+         m_integer.dump_to_pp (pp);
+         pp_string (pp, " bits");
+       }
+      break;
+    }
+}
+
+template<>
+DEBUG_FUNCTION void
+integer_with_unit<units::bit>::dump () const
+{
+  pretty_printer pp;
+  pp.set_output_stream (stderr);
+  dump_to_pp (&pp, units::role::size);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+template<>
+DEBUG_FUNCTION void
+integer_with_unit<units::byte>::dump () const
+{
+  pretty_printer pp;
+  pp.set_output_stream (stderr);
+  dump_to_pp (&pp, units::role::size);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+template<>
+std::unique_ptr<json::object>
+integer_with_unit<units::bit>::to_json () const
+{
+  auto obj = std::make_unique<json::object> ();
+  obj->set ("integer", m_integer.to_json ());
+  obj->set_string ("unit", "bits");
+  return obj;
+}
+
+template<>
+std::unique_ptr<json::object>
+integer_with_unit<units::byte>::to_json () const
+{
+  auto obj = std::make_unique<json::object> ();
+  obj->set ("integer", m_integer.to_json ());
+  obj->set_string ("unit", "bytes");
+  return obj;
+}
+
+template<typename Unit>
+std::unique_ptr<diagnostics::markup::node>
+integer_with_unit<Unit>::
+make_markup_node () const
+{
+  return m_integer.make_markup_node ();
+}
+
+template
+std::unique_ptr<diagnostics::markup::node>
+integer_with_unit<units::bit>::
+make_markup_node () const;
+
+template
+std::unique_ptr<diagnostics::markup::node>
+integer_with_unit<units::byte>::
+make_markup_node () const;
+
+bool
+maybe_bytes_from_bits (integer_with_unit<units::bit> bits,
+                      integer_with_unit<units::byte> *out)
+{
+  if (bits.floor_mod (integer_with_unit<units::bit> (BITS_PER_UNIT))
+      != 0_bits)
+    return false;
+  *out = integer_with_unit<units::byte>
+    (bits.get_integer ().floor_div (BITS_PER_UNIT));
+  return true;
+}
+
+integer_with_unit<units::bit>
+convert_from_bit_offset_t (bit_offset_t bits)
+{
+  unsigned HOST_WIDE_INT uhwi = bits.to_uhwi ();
+  return integer_with_unit<units::bit> (uhwi);
+}
+
+integer_with_unit<units::bit>
+convert_from_bit_size_t (bit_size_t bits)
+{
+  unsigned HOST_WIDE_INT uhwi = bits.to_uhwi ();
+  return integer_with_unit<units::bit> (uhwi);
+}
+
+integer_with_unit<units::byte>
+convert_from_byte_offset_t (byte_offset_t bytes)
+{
+  unsigned HOST_WIDE_INT uhwi = bytes.to_uhwi ();
+  return integer_with_unit<units::byte> (uhwi);
+}
+
+integer_with_unit<units::byte>
+convert_from_byte_size_t (byte_size_t bytes)
+{
+  unsigned HOST_WIDE_INT uhwi = bytes.to_uhwi ();
+  return integer_with_unit<units::byte> (uhwi);
+}
+
+bit_offset_t
+convert_to_bit_offset_t (integer_with_unit<units::bit> new_val)
+{
+  return bit_offset_t (new_val.as_long ());
+}
+
+bit_size_t
+convert_to_bit_size_t (integer_with_unit<units::bit> new_val)
+{
+  return bit_offset_t (new_val.as_unsigned_long ());
+}
+
+byte_offset_t
+convert_to_byte_offset_t (integer_with_unit<units::byte> new_val)
+{
+  gcc_assert (new_val >= 0_bytes);
+  return byte_offset_t (new_val.get_integer ().as_unsigned_long ());
+}
+
+byte_size_t
+convert_to_byte_size_t (integer_with_unit<units::byte> new_val)
+{
+  gcc_assert (new_val >= 0_bytes);
+  return byte_offset_t (new_val.get_integer ().as_unsigned_long ());
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Unit-independent tests.  */
+
+template<typename Unit>
+static void
+test_rule_of_5 ()
+{
+  integer_with_unit<Unit> a;
+  ASSERT_EQ (a.get_integer (), 0);
+
+  integer_with_unit<Unit> b (42);
+  ASSERT_EQ (b.get_integer (), 42);
+
+  integer_with_unit<Unit> c (b);
+  ASSERT_EQ (c.get_integer (), 42);
+  // Check that writes to b don't affect c after the copy:
+  b = integer_with_unit<Unit> (43);
+  ASSERT_EQ (b.get_integer (), 43);
+  ASSERT_EQ (c.get_integer (), 42);
+
+  integer_with_unit<Unit> d (std::move (c));
+  ASSERT_EQ (d.get_integer (), 42);
+  ASSERT_EQ (c.get_integer (), 0);
+
+  c = integer_with_unit<Unit> (1066);
+  ASSERT_EQ (c.get_integer (), 1066);
+
+  a = std::move (d);
+  ASSERT_EQ (a.get_integer (), 42);
+}
+
+/* Test addition, for scalars of the given unit.  */
+
+template<typename Unit>
+static void
+test_addition ()
+{
+  ASSERT_EQ (integer_with_unit<Unit> (1), integer_with_unit<Unit> (1));
+  ASSERT_NE (integer_with_unit<Unit> (1), integer_with_unit<Unit> (2));
+
+  /* Addition.  */
+  ASSERT_EQ (integer_with_unit<Unit> (4) + integer_with_unit<Unit> (3),
+            integer_with_unit<Unit> (7));
+
+  /* Subtraction.  */
+  ASSERT_EQ (integer_with_unit<Unit> (4) - integer_with_unit<Unit> (3),
+            integer_with_unit<Unit> (1));
+
+  /* Additive identity.  */
+  ASSERT_EQ (integer_with_unit<Unit> (3) + integer_with_unit<Unit> (0),
+            integer_with_unit<Unit> (3));
+  ASSERT_EQ (integer_with_unit<Unit> (2) - integer_with_unit<Unit> (0),
+            integer_with_unit<Unit> (2));
+
+  /* Negative values.   */
+  ASSERT_EQ (integer_with_unit<Unit> (4) - integer_with_unit<Unit> (5),
+            integer_with_unit<Unit> (-1));
+  ASSERT_EQ (integer_with_unit<Unit> (-1) - integer_with_unit<Unit> (-3),
+            integer_with_unit<Unit> (2));
+}
+
+/* Test operator++, for scalars of the given unit.  */
+
+template<typename Unit>
+static void
+test_auto_inc ()
+{
+  integer_with_unit<Unit> i (0);
+  ASSERT_EQ (i, integer_with_unit<Unit> (0));
+
+  ++i;
+  ASSERT_EQ (i, integer_with_unit<Unit> (1));
+
+  ++i;
+  ASSERT_EQ (i, integer_with_unit<Unit> (2));
+}
+
+
+/* Test multiplication, for scalars of the given unit by unitless integers.  */
+
+template<typename Unit>
+static void
+test_scaling ()
+{
+  ASSERT_EQ (integer_with_unit<Unit> (5) * 2,
+            integer_with_unit<Unit> (10));
+
+  /* Identity.  */
+  ASSERT_EQ (integer_with_unit<Unit> (7) * 1,
+            integer_with_unit<Unit> (7));
+
+  /* Zero.  */
+  ASSERT_EQ (integer_with_unit<Unit> (4) * 0,
+            integer_with_unit<Unit> (0));
+}
+
+template<typename Unit>
+static void
+test_division ()
+{
+  ASSERT_EQ (integer_with_unit<Unit> (0)
+            .floor_div (integer_with_unit<Unit> (10)),
+            integer (0));
+  ASSERT_EQ (integer_with_unit<Unit> (24)
+            .floor_div (integer_with_unit<Unit> (10)),
+            integer (2));
+  ASSERT_EQ (integer_with_unit<Unit> (3)
+            .floor_div (integer_with_unit<Unit> (1)),
+            integer (3));
+  ASSERT_EQ (integer_with_unit<Unit> (-8)
+            .floor_div (integer_with_unit<Unit> (10)),
+            integer (-1));
+}
+
+template<typename Unit>
+static void
+test_modulus ()
+{
+  ASSERT_EQ (integer_with_unit<Unit> (0)
+            .floor_mod (integer_with_unit<Unit> (10)),
+            integer_with_unit<Unit> (0));
+  ASSERT_EQ (integer_with_unit<Unit> (24)
+            .floor_mod (integer_with_unit<Unit> (10)),
+            integer_with_unit<Unit> (4));
+  ASSERT_EQ (integer_with_unit<Unit> (3)
+            .floor_mod (integer_with_unit<Unit> (1)),
+            integer_with_unit<Unit> (0));
+  ASSERT_EQ (integer_with_unit<Unit> (-8)
+            .floor_mod (integer_with_unit<Unit> (10)),
+            integer_with_unit<Unit> (2));
+}
+
+template<typename Unit>
+static void
+test_dumping ();
+
+template<>
+void
+test_dumping<units::bit> ()
+{
+  {
+    auto seven_bits = 7_bits;
+    {
+      pretty_printer pp;
+      seven_bits.dump_to_pp (&pp, units::role::offset);
+      ASSERT_STREQ (pp_formatted_text (&pp), "bit 7");
+    }
+    {
+      pretty_printer pp;
+      seven_bits.dump_to_pp (&pp, units::role::size);
+      ASSERT_STREQ (pp_formatted_text (&pp), "7 bits");
+    }
+  }
+
+  {
+    auto one_bit = 1_bit;
+    {
+      pretty_printer pp;
+      one_bit.dump_to_pp (&pp, units::role::offset);
+      ASSERT_STREQ (pp_formatted_text (&pp), "bit 1");
+    }
+    {
+      pretty_printer pp;
+      one_bit.dump_to_pp (&pp, units::role::size);
+      ASSERT_STREQ (pp_formatted_text (&pp), "1 bit");
+    }
+  }
+}
+
+template<>
+void
+test_dumping<units::byte> ()
+{
+  {
+    auto seven_bytes = 7_bytes;
+    {
+      pretty_printer pp;
+      seven_bytes.dump_to_pp (&pp, units::role::offset);
+      ASSERT_STREQ (pp_formatted_text (&pp), "byte 7");
+    }
+    {
+      pretty_printer pp;
+      seven_bytes.dump_to_pp (&pp, units::role::size);
+      ASSERT_STREQ (pp_formatted_text (&pp), "7 bytes");
+    }
+  }
+
+  {
+    auto one_byte = 1_byte;
+    {
+      pretty_printer pp;
+      one_byte.dump_to_pp (&pp, units::role::offset);
+      ASSERT_STREQ (pp_formatted_text (&pp), "byte 1");
+    }
+    {
+      pretty_printer pp;
+      one_byte.dump_to_pp (&pp, units::role::size);
+      ASSERT_STREQ (pp_formatted_text (&pp), "1 byte");
+    }
+  }
+}
+
+/* Run all per-unit tests, for Unit.  */
+
+template<typename Unit>
+static void
+per_unit_scalar_tests ()
+{
+  test_rule_of_5<Unit> ();
+  test_addition<Unit> ();
+  test_auto_inc<Unit> ();
+  test_scaling<Unit> ();
+  test_division<Unit> ();
+  test_modulus<Unit> ();
+  test_dumping<Unit> ();
+}
+
+static void
+test_to_json ()
+{
+  ASSERT_JSON_PRINT_EQ (*(32_bits).to_json (), true,
+                       ("{\"integer\": \"32\",\n"
+                        " \"unit\": \"bits\"}"));
+  ASSERT_JSON_PRINT_EQ (*(1024_bytes).to_json (), true,
+                       ("{\"integer\": \"1024\",\n"
+                        " \"unit\": \"bytes\"}"));
+}
+
+static void
+test_comparison_of_compatible_mixed_units ()
+{
+  ASSERT_EQ (integer_with_unit<units::bit> (8),
+            integer_with_unit<units::byte> (1));
+  ASSERT_NE (integer_with_unit<units::bit> (7),
+            integer_with_unit<units::byte> (1));
+  ASSERT_NE (integer_with_unit<units::bit> (9),
+            integer_with_unit<units::byte> (1));
+}
+
+static void
+test_addition_of_compatible_mixed_units ()
+{
+  ASSERT_EQ ((integer_with_unit<units::bit> (8)
+             + integer_with_unit<units::byte> (1)),
+            integer_with_unit<units::bit> (16));
+
+  ASSERT_EQ ((integer_with_unit<units::byte> (1)
+             - integer_with_unit<units::bit> (2)),
+            integer_with_unit<units::bit> (6));
+}
+
+/* Unit-dependent tests.  */
+
+static void
+test_udls ()
+{
+  ASSERT_EQ (8_bits, 1_byte);
+  ASSERT_NE (7_bits, 1_byte);
+  ASSERT_NE (9_bits, 1_byte);
+  ASSERT_EQ (1_byte + 1_bit, 9_bits);
+  ASSERT_EQ (1_byte - 2_bits, 6_bits);
+  ASSERT_EQ ((5_bits * 4) - 2_bytes, 4_bits);
+}
+
+/* Consider:
+   |
+   |  struct coord { uint32_t x; uint32_t y; };
+   |  struct triangle { coord m_vertices[3]; };
+   |
+   and computing sizeof (triangle).  */
+
+static void
+test_sizeof_triangle ()
+{
+  /* In bytes.  */
+  {
+    auto sizeof_x = 32_bits;
+    auto sizeof_y = 32_bits;
+
+    auto sizeof_coord = sizeof_x + sizeof_y;
+    ASSERT_EQ (sizeof_coord, 64_bits);
+
+    auto sizeof_triangle = sizeof_coord * 3;
+    ASSERT_EQ (sizeof_triangle, 192_bits);
+  }
+
+  /* In bits.  */
+  {
+    auto sizeof_x = 4_bytes;
+    auto sizeof_y = 4_bytes;
+
+    auto sizeof_coord = sizeof_x + sizeof_y;
+    ASSERT_EQ (sizeof_coord, 8_bytes);
+
+    auto sizeof_triangle = sizeof_coord * 3;
+    ASSERT_EQ (sizeof_triangle, 24_bytes);
+  }
+}
+
+static void
+test_round_bit_to_lowest_byte ()
+{
+  ASSERT_EQ (round_bit_to_lowest_byte (0_bits), 0_bytes);
+  ASSERT_EQ (round_bit_to_lowest_byte (1_bit), 0_bytes);
+  ASSERT_EQ (round_bit_to_lowest_byte (7_bits), 0_bytes);
+  ASSERT_EQ (round_bit_to_lowest_byte (8_bits), 1_byte);
+  ASSERT_EQ (round_bit_to_lowest_byte (15_bits), 1_byte);
+  ASSERT_EQ (round_bit_to_lowest_byte (16_bits), 2_bytes);
+  ASSERT_EQ (round_bit_to_lowest_byte (23_bits), 2_bytes);
+  ASSERT_EQ (round_bit_to_lowest_byte (24_bits), 3_bytes);
+}
+
+static void
+test_bit_within_byte ()
+{
+  ASSERT_EQ (bit_within_byte (0_bits), 0_bits);
+  ASSERT_EQ (bit_within_byte (1_bit), 1_bit);
+  ASSERT_EQ (bit_within_byte (7_bits), 7_bits);
+  ASSERT_EQ (bit_within_byte (8_bits), 0_bits);
+  ASSERT_EQ (bit_within_byte (15_bits), 7_bits);
+  ASSERT_EQ (bit_within_byte (16_bits), 0_bits);
+  ASSERT_EQ (bit_within_byte (23_bits), 7_bits);
+  ASSERT_EQ (bit_within_byte (24_bits), 0_bits);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_integer_with_unit_cc_tests ()
+{
+  per_unit_scalar_tests<units::bit> ();
+  per_unit_scalar_tests<units::byte> ();
+
+  test_to_json ();
+
+  test_comparison_of_compatible_mixed_units ();
+  test_addition_of_compatible_mixed_units ();
+
+  test_udls ();
+
+  test_sizeof_triangle ();
+
+  test_round_bit_to_lowest_byte ();
+  test_bit_within_byte ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/integer-with-unit.h b/gcc/analyzer/integer-with-unit.h
new file mode 100644
index 00000000000..daf59537294
--- /dev/null
+++ b/gcc/analyzer/integer-with-unit.h
@@ -0,0 +1,403 @@
+/* Unit-safe arbitrary precision integers (e.g. "32 bytes").
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   Contributed by David Malcolm <[email protected]>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_INTEGER_WITH_UNIT_H
+#define GCC_ANALYZER_INTEGER_WITH_UNIT_H
+
+#include "analyzer/integer.h"
+#include "analyzer/units.h"
+
+namespace ana {
+
+/* A unit-safe arbitrary precision integer e.g. "32_bytes" or "5_bits".  */
+
+template<typename Unit>
+class integer_with_unit
+{
+public:
+  static constexpr const char *dump_type_name = Unit::dump_type_name;
+
+  integer_with_unit () : m_integer () {}
+
+  explicit integer_with_unit (int i) : m_integer (i) {}
+
+  explicit integer_with_unit (integer i) : m_integer (std::move (i)) {}
+
+  const integer &get_integer () const { return m_integer; }
+
+  void
+  dump_to_pp (pretty_printer *pp,
+             units::role role = units::role::generic) const;
+  void
+  dump () const;
+
+  std::unique_ptr<json::object>
+  to_json () const;
+
+  std::unique_ptr<diagnostics::markup::node>
+  make_markup_node () const;
+
+  bool
+  is_zero_p () const { return m_integer.is_zero_p (); }
+
+  bool
+  is_one_p () const { return m_integer.is_one_p (); }
+
+  static int
+  cmp (const integer_with_unit<Unit> &lhs,
+       const integer_with_unit<Unit> &rhs)
+  {
+    return integer::cmp (lhs.m_integer, rhs.m_integer);
+  }
+
+  integer_with_unit<Unit>&
+  operator+= (const integer_with_unit<Unit> &rhs)
+  {
+    m_integer += rhs.m_integer;
+    return *this;
+  }
+
+  integer_with_unit<Unit>&
+  operator-= (const integer_with_unit<Unit> &rhs)
+  {
+    m_integer -= rhs.m_integer;
+    return *this;
+  }
+
+  integer_with_unit<Unit>&
+  operator++ ()
+  {
+    m_integer += 1;
+    return *this;
+  }
+
+  bool
+  operator< (const integer_with_unit<Unit> &rhs) const
+  {
+    return m_integer < rhs.m_integer;
+  }
+
+  bool
+  operator<= (const integer_with_unit<Unit> &rhs) const
+  {
+    return m_integer <= rhs.m_integer;
+  }
+
+  bool
+  operator> (const integer_with_unit<Unit> &rhs) const
+  {
+    return m_integer > rhs.m_integer;
+  }
+
+  bool
+  operator>= (const integer_with_unit<Unit> &rhs) const
+  {
+    return m_integer >= rhs.m_integer;
+  }
+
+  integer_with_unit<Unit>
+  operator- () const
+  {
+    return integer_with_unit<Unit> (-m_integer);
+  }
+
+  /* Division.  */
+
+  integer
+  floor_div (const integer_with_unit<Unit> &other) const
+  {
+    return get_integer ().floor_div (other.get_integer ());
+  }
+
+  /* Modulus.  */
+
+  integer_with_unit<Unit>
+  floor_mod (const integer_with_unit<Unit> &other) const
+  {
+    return integer_with_unit<Unit>
+      (get_integer ().floor_mod (other.get_integer ()));
+  }
+
+  long
+  as_long () const
+  {
+    return m_integer.as_long ();
+  }
+
+  unsigned long
+  as_unsigned_long () const
+  {
+    return m_integer.as_unsigned_long ();
+  }
+
+private:
+  integer m_integer;
+};
+
+/* User-defined literal operators.  */
+
+inline integer_with_unit<units::bit>
+operator ""_bit (unsigned long long v)
+{
+  gcc_checking_assert (v == 1);
+  return integer_with_unit<units::bit> (v);
+}
+
+inline integer_with_unit<units::bit>
+operator ""_bits (unsigned long long v)
+{
+  gcc_checking_assert (v != 1);
+  return integer_with_unit<units::bit> (v);
+}
+
+inline integer_with_unit<units::byte>
+operator ""_byte (unsigned long long v)
+{
+  gcc_checking_assert (v == 1);
+  return integer_with_unit<units::byte> (v);
+}
+
+inline integer_with_unit<units::byte>
+operator ""_bytes (unsigned long long v)
+{
+  gcc_checking_assert (v != 1);
+  return integer_with_unit<units::byte> (v);
+}
+
+/* Explicit conversions.  */
+
+inline integer_with_unit<units::bit>
+bits_from_bytes (integer_with_unit<units::byte> bytes)
+{
+  return integer_with_unit<units::bit> (bytes.get_integer () * BITS_PER_UNIT);
+}
+
+bool
+maybe_bytes_from_bits (integer_with_unit<units::bit> bits,
+                      integer_with_unit<units::byte> *out);
+
+/* Overloaded operators.  */
+
+/* Comparisons of like units.  */
+
+template<typename Unit>
+inline bool
+operator== (const integer_with_unit<Unit> &lhs,
+           const integer_with_unit<Unit> &rhs)
+{
+  return lhs.get_integer () == rhs.get_integer ();
+}
+
+template<typename Unit>
+inline bool
+operator!= (const integer_with_unit<Unit> &lhs,
+           const integer_with_unit<Unit> &rhs)
+{
+  return lhs.get_integer () != rhs.get_integer ();
+}
+
+/* Comparisons of compatible units, implicitly converting to bits.  */
+
+inline bool
+operator== (const integer_with_unit<units::bit> &lhs,
+           const integer_with_unit<units::byte> &rhs)
+{
+  return lhs == bits_from_bytes (rhs);
+}
+
+inline bool
+operator== (const integer_with_unit<units::byte> &lhs,
+           const integer_with_unit<units::bit> &rhs)
+{
+  return bits_from_bytes (lhs) == rhs;
+}
+
+inline bool
+operator!= (const integer_with_unit<units::bit> &lhs,
+           const integer_with_unit<units::byte> &rhs)
+{
+  return lhs != bits_from_bytes (rhs);
+}
+
+inline bool
+operator!= (const integer_with_unit<units::byte> &lhs,
+           const integer_with_unit<units::bit> &rhs)
+{
+  return bits_from_bytes (lhs) != rhs;
+}
+
+/* Addition of integer_with_unit of same unit.  */
+
+template<typename Unit>
+inline integer_with_unit<Unit>
+operator+ (const integer_with_unit<Unit> &lhs,
+          const integer_with_unit<Unit> &rhs)
+{
+  return integer_with_unit<Unit> (lhs.get_integer () + rhs.get_integer ());
+}
+
+/* Addition of integer_with_unit of compatible types, implicitly converting to 
bits.  */
+
+inline integer_with_unit<units::bit>
+operator+ (const integer_with_unit<units::bit> &lhs,
+          const integer_with_unit<units::byte> &rhs)
+{
+  return lhs + bits_from_bytes (rhs);
+}
+
+inline integer_with_unit<units::bit>
+operator+ (const integer_with_unit<units::byte> &lhs,
+          const integer_with_unit<units::bit> &rhs)
+{
+  return bits_from_bytes (lhs) + rhs;
+}
+
+/* Negation.  */
+
+template<typename Unit>
+inline integer_with_unit<Unit>
+operator- (const integer_with_unit<Unit> &i)
+{
+  return integer_with_unit<Unit> (-(i.get_integer ()));
+}
+
+/* Subtraction of integer_with_unit of same kind.  */
+
+template<typename Unit>
+inline integer_with_unit<Unit>
+operator- (const integer_with_unit<Unit> &lhs,
+          const integer_with_unit<Unit> &rhs)
+{
+  return integer_with_unit<Unit> (lhs.get_integer () - rhs.get_integer ());
+}
+
+/* Subtraction of integer_with_unit of compatible types,
+   implicitly converting to bits.  */
+
+integer_with_unit<units::bit>
+inline operator- (const integer_with_unit<units::bit> &lhs,
+                 const integer_with_unit<units::byte> &rhs)
+{
+  return lhs - bits_from_bytes (rhs);
+}
+
+inline integer_with_unit<units::bit>
+operator- (const integer_with_unit<units::byte> &lhs,
+          const integer_with_unit<units::bit> &rhs)
+{
+  return bits_from_bytes (lhs) - rhs;
+}
+
+/* Multiplication of integer_with_unit by a unitless value.  */
+
+template<typename Unit>
+inline integer_with_unit<Unit>
+operator* (const integer_with_unit<Unit> &lhs,
+          const integer &rhs)
+{
+  return integer_with_unit<Unit> (lhs.get_integer () * rhs);
+}
+
+/* Multiplication of unitless value by a integer_with_unit.  */
+
+template<typename Unit>
+inline integer_with_unit<Unit>
+operator* (const integer &lhs,
+          const integer_with_unit<Unit> &rhs)
+{
+  return integer_with_unit<Unit> (lhs * rhs.get_integer ());
+}
+
+using concrete_bit_size = integer_with_unit<units::bit>;
+using concrete_bit_offset = integer_with_unit<units::bit>;
+using concrete_byte_size = integer_with_unit<units::byte>;
+using concrete_byte_offset = integer_with_unit<units::byte>;
+
+inline concrete_byte_offset
+round_bit_to_lowest_byte (concrete_bit_offset bit_offset)
+{
+  return concrete_byte_offset
+    (bit_offset.get_integer ().floor_div (BITS_PER_UNIT));
+}
+
+inline concrete_bit_offset
+bit_within_byte (concrete_bit_offset bit_offset)
+{
+  return concrete_bit_offset
+    (bit_offset.get_integer ().floor_mod (BITS_PER_UNIT));
+}
+
+extern bool
+get_type_size (const_tree type, concrete_bit_size *out);
+
+/* Functions for converting from older representation.
+
+   Ultimately we'll want {bit,byte}_{offset,size}_t to be
+   integer_with_unit<units::{bit,byte}>
+   but for now these should help migrate the code.  */
+
+concrete_bit_offset
+convert_from_bit_offset_t (bit_offset_t);
+
+concrete_bit_size
+convert_from_bit_size_t (bit_size_t);
+
+integer_with_unit<units::byte>
+convert_from_byte_offset_t (byte_offset_t);
+
+integer_with_unit<units::byte>
+convert_from_byte_size_t (byte_size_t);
+
+/* Functions for converting to older representation.  */
+
+bit_offset_t
+convert_to_bit_offset_t (concrete_bit_offset);
+
+bit_size_t
+convert_to_bit_size_t (concrete_bit_size);
+
+byte_offset_t
+convert_to_byte_offset_t (concrete_byte_offset);
+
+byte_size_t
+convert_to_byte_size_t (concrete_byte_size);
+
+template <typename ConcreteValueType>
+inline bool
+eval_comparison_p (ConcreteValueType a,
+                  enum tree_code op,
+                  ConcreteValueType b)
+{
+  switch (op)
+    {
+    default:
+      gcc_unreachable ();
+    case EQ_EXPR: return a == b;
+    case NE_EXPR: return a != b;
+    case GE_EXPR: return a >= b;
+    case LE_EXPR: return a <= b;
+    case GT_EXPR: return a > b;
+    case LT_EXPR: return a < b;
+    }
+}
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_INTEGER_WITH_UNIT_H */
diff --git a/gcc/analyzer/units.h b/gcc/analyzer/units.h
new file mode 100644
index 00000000000..a0117323fe3
--- /dev/null
+++ b/gcc/analyzer/units.h
@@ -0,0 +1,69 @@
+/* Traits for expressing units and dimensions in the type system.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   Contributed by David Malcolm <[email protected]>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_UNITS_H
+#define GCC_ANALYZER_UNITS_H
+
+/* Traits types for distinguishing different kinds of scalars
+   and ranges, so that we get a type error if we mess up our units.  */
+
+namespace ana {
+namespace units {
+
+/* Traits type representing the size of something in memory,
+   measured in *bits*.  */
+
+struct bit
+{
+  // Forbid creating instances: this is just a trait
+  bit () = delete;
+
+  static constexpr const char *dump_type_name = "bits";
+  static constexpr const char *singular_dump_name = "bit";
+  static constexpr const char *plural_dump_name = "bit";
+};
+
+/* Traits type representing the size of something in memory,
+   measured in *bytes*.  */
+
+struct byte
+{
+  // Forbid creating instances: this is just a trait
+  byte () = delete;
+
+  static constexpr const char *dump_type_name = "bytes";
+  static constexpr const char *singular_dump_name = "byte";
+  static constexpr const char *plural_dump_name = "bytes";
+};
+
+/* Enum for distinguishing different roles of a scalar
+   so e.g. "10" vs "byte 10" for an offset vs "10 bytes" for a size.  */
+
+enum class role
+{
+  generic,
+  offset,
+  size
+};
+
+} // namespace ana::units
+} // namespace ana
+
+#endif /* GCC_ANALYZER_UNITS_H */
-- 
2.56.0

Reply via email to