blob: daf5953729490d46e910585eb6cae3bcf50d5fa7 [file]
/* Unit-safe arbitrary precision integers (e.g. "32 bytes").
Copyright (C) 2026 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
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 */