/* Polynomial integer classes.
Copyright (C) 2014-2024 Free Software Foundation, Inc.
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
. */
/* This file provides a representation of sizes and offsets whose exact
values depend on certain runtime properties. The motivating example
is the Arm SVE ISA, in which the number of vector elements is only
known at runtime. See doc/poly-int.texi for more details.
Tests for poly-int.h are located in testsuite/gcc.dg/plugin,
since they are too expensive (in terms of binary size) to be
included as selftests. */
#ifndef HAVE_POLY_INT_H
#define HAVE_POLY_INT_H
template class poly_int;
/* poly_coeff_traiits describes the properties of a poly_int
coefficient type T:
- poly_coeff_traits::rank is less than poly_coeff_traits::rank
if T1 can promote to T2. For C-like types the rank is:
(2 * number of bytes) + (unsigned ? 1 : 0)
wide_ints don't have a normal rank and so use a value of INT_MAX.
Any fixed-width integer should be promoted to wide_int if possible
and lead to an error otherwise.
- poly_coeff_traits::int_type is the type to which an integer
literal should be cast before comparing it with T.
- poly_coeff_traits::precision is the number of bits that T can hold.
- poly_coeff_traits::signedness is:
0 if T is unsigned
1 if T is signed
-1 if T has no inherent sign (as for wide_int).
- poly_coeff_traits::max_value, if defined, is the maximum value of T.
- poly_coeff_traits::result is a type that can hold results of
operations on T. This is different from T itself in cases where T
is the result of an accessor like wi::to_offset.
- poly_coeff_traits::init_cast::type is the type to which
an argument of type Arg should be casted before being used to
initialize a coefficient of type T. */
template::precision_type>
struct poly_coeff_traits;
template
struct poly_coeff_traits
{
typedef T result;
typedef T int_type;
static const int signedness = (T (0) >= T (-1));
static const int precision = sizeof (T) * CHAR_BIT;
static const T max_value = (signedness
? ((T (1) << (precision - 2))
+ ((T (1) << (precision - 2)) - 1))
: T (-1));
static const int rank = sizeof (T) * 2 + !signedness;
template
struct init_cast { using type = T; };
};
template
struct poly_coeff_traits
{
typedef T result;
typedef int int_type;
static const int signedness = -1;
static const int precision = WIDE_INT_MAX_PRECISION;
static const int rank = INT_MAX;
template
struct init_cast { using type = const Arg &; };
};
template
struct poly_coeff_traits
{
typedef WI_UNARY_RESULT (T) result;
typedef int int_type;
/* These types are always signed. */
static const int signedness = 1;
static const int precision = wi::int_traits::precision;
static const int rank = precision * 2 / CHAR_BIT;
template
struct init_cast { using type = const Arg &; };
};
template
struct poly_coeff_traits
{
typedef WI_UNARY_RESULT (T) result;
typedef int int_type;
/* These types are always signed. */
static const int signedness = 1;
static const int precision = wi::int_traits::precision;
static const int rank = precision * 2 / CHAR_BIT;
template
struct init_cast { using type = const Arg &; };
};
/* Information about a pair of coefficient types. */
template
struct poly_coeff_pair_traits
{
/* True if T1 can represent all the values of T2.
Either:
- T1 should be a type with the same signedness as T2 and no less
precision. This allows things like int16_t -> int16_t and
uint32_t -> uint64_t.
- T1 should be signed, T2 should be unsigned, and T1 should be
wider than T2. This allows things like uint16_t -> int32_t.
This rules out cases in which T1 has less precision than T2 or where
the conversion would reinterpret the top bit. E.g. int16_t -> uint32_t
can be dangerous and should have an explicit cast if deliberate. */
static const bool lossless_p = (poly_coeff_traits::signedness
== poly_coeff_traits::signedness
? (poly_coeff_traits::precision
>= poly_coeff_traits::precision)
: (poly_coeff_traits::signedness == 1
&& poly_coeff_traits::signedness == 0
&& (poly_coeff_traits::precision
> poly_coeff_traits::precision)));
/* 0 if T1 op T2 should promote to HOST_WIDE_INT,
1 if T1 op T2 should promote to unsigned HOST_WIDE_INT,
2 if T1 op T2 should use wide-int rules. */
#define RANK(X) poly_coeff_traits::rank
static const int result_kind
= ((RANK (T1) <= RANK (HOST_WIDE_INT)
&& RANK (T2) <= RANK (HOST_WIDE_INT))
? 0
: (RANK (T1) <= RANK (unsigned HOST_WIDE_INT)
&& RANK (T2) <= RANK (unsigned HOST_WIDE_INT))
? 1 : 2);
#undef RANK
};
/* SFINAE class that makes T3 available as "type" if T2 can represent all the
values in T1. */
template::lossless_p>
struct if_lossless;
template
struct if_lossless
{
typedef T3 type;
};
/* poly_int_traits describes an integer type T that might be polynomial
or non-polynomial:
- poly_int_traits::is_poly is true if T is a poly_int-based type
and false otherwise.
- poly_int_traits::num_coeffs gives the number of coefficients in T
if T is a poly_int and 1 otherwise.
- poly_int_traits::coeff_type gives the coefficent type of T if T
is a poly_int and T itself otherwise
- poly_int_traits::int_type is a shorthand for
typename poly_coeff_traits::int_type. */
template
struct poly_int_traits
{
static const bool is_poly = false;
static const unsigned int num_coeffs = 1;
typedef T coeff_type;
typedef typename poly_coeff_traits::int_type int_type;
};
template
struct poly_int_traits >
{
static const bool is_poly = true;
static const unsigned int num_coeffs = N;
typedef C coeff_type;
typedef typename poly_coeff_traits::int_type int_type;
};
/* SFINAE class that makes T2 available as "type" if T1 is a non-polynomial
type. */
template::is_poly>
struct if_nonpoly {};
template
struct if_nonpoly
{
typedef T2 type;
};
/* SFINAE class that makes T3 available as "type" if both T1 and T2 are
non-polynomial types. */
template::is_poly,
bool is_poly2 = poly_int_traits::is_poly>
struct if_nonpoly2 {};
template
struct if_nonpoly2
{
typedef T3 type;
};
/* SFINAE class that makes T2 available as "type" if T1 is a polynomial
type. */
template::is_poly>
struct if_poly {};
template
struct if_poly
{
typedef T2 type;
};
/* poly_result describes the result of an operation on two
types T1 and T2, where at least one of the types is polynomial:
- poly_result::type gives the result type for the operation.
The intention is to provide normal C-like rules for integer ranks,
except that everything smaller than HOST_WIDE_INT promotes to
HOST_WIDE_INT.
- poly_result::cast is the type to which an operand of type
T1 should be cast before doing the operation, to ensure that
the operation is done at the right precision. Casting to
poly_result::type would also work, but casting to this
type is more efficient. */
template::result_kind>
struct poly_result;
/* Promote pair to HOST_WIDE_INT. */
template
struct poly_result
{
typedef HOST_WIDE_INT type;
/* T1 and T2 are primitive types, so cast values to T before operating
on them. */
typedef type cast;
};
/* Promote pair to unsigned HOST_WIDE_INT. */
template
struct poly_result
{
typedef unsigned HOST_WIDE_INT type;
/* T1 and T2 are primitive types, so cast values to T before operating
on them. */
typedef type cast;
};
/* Use normal wide-int rules. */
template
struct poly_result
{
typedef WI_BINARY_RESULT (T1, T2) type;
/* Don't cast values before operating on them; leave the wi:: routines
to handle promotion as necessary. */
typedef const T1 &cast;
};
/* The coefficient type for the result of a binary operation on two
poly_ints, the first of which has coefficients of type C1 and the
second of which has coefficients of type C2. */
#define POLY_POLY_COEFF(C1, C2) typename poly_result::type
/* Enforce that T2 is non-polynomial and provide the cofficient type of
the result of a binary operation in which the first operand is a
poly_int with coefficients of type C1 and the second operand is
a constant of type T2. */
#define POLY_CONST_COEFF(C1, T2) \
POLY_POLY_COEFF (C1, typename if_nonpoly::type)
/* Likewise in reverse. */
#define CONST_POLY_COEFF(T1, C2) \
POLY_POLY_COEFF (typename if_nonpoly::type, C2)
/* The result type for a binary operation on poly_int and
poly_int. */
#define POLY_POLY_RESULT(N, C1, C2) poly_int
/* Enforce that T2 is non-polynomial and provide the result type
for a binary operation on poly_int and T2. */
#define POLY_CONST_RESULT(N, C1, T2) poly_int
/* Enforce that T1 is non-polynomial and provide the result type
for a binary operation on T1 and poly_int. */
#define CONST_POLY_RESULT(N, T1, C2) poly_int
/* Enforce that T1 and T2 are non-polynomial and provide the result type
for a binary operation on T1 and T2. */
#define CONST_CONST_RESULT(N, T1, T2) \
POLY_POLY_COEFF (typename if_nonpoly::type, \
typename if_nonpoly::type)
/* The type to which a coefficient of type C1 should be cast before
using it in a binary operation with a coefficient of type C2. */
#define POLY_CAST(C1, C2) typename poly_result::cast
/* Provide the coefficient type for the result of T1 op T2, where T1
and T2 can be polynomial or non-polynomial. */
#define POLY_BINARY_COEFF(T1, T2) \
typename poly_result::coeff_type, \
typename poly_int_traits::coeff_type>::type
/* The type to which an integer constant should be cast before
comparing it with T. */
#define POLY_INT_TYPE(T) typename poly_int_traits::int_type
/* RES is a poly_int result that has coefficients of type C and that
is being built up a coefficient at a time. Set coefficient number I
to VALUE in the most efficient way possible.
For primitive C it is better to assign directly, since it avoids
any further calls and so is more efficient when the compiler is
built at -O0. But for wide-int based C it is better to construct
the value in-place. This means that calls out to a wide-int.cc
routine can take the address of RES rather than the address of
a temporary.
The dummy self-comparison against C * is just a way of checking
that C gives the right type. */
#define POLY_SET_COEFF(C, RES, I, VALUE) \
((void) (&(RES).coeffs[0] == (C *) (void *) &(RES).coeffs[0]), \
wi::int_traits::precision_type == wi::FLEXIBLE_PRECISION \
? (void) ((RES).coeffs[I] = VALUE) \
: (void) ((RES).coeffs[I].~C (), new (&(RES).coeffs[I]) C (VALUE)))
/* Number of bits needed to represent maximum value of
NUM_POLY_INT_COEFFS defined by any target. */
#define MAX_NUM_POLY_INT_COEFFS_BITS 2
/* poly_int_full and poly_int_hungry are used internally within poly_int
for delegated initializers. poly_int_full indicates that a parameter
pack has enough elements to initialize every coefficient. poly_int_hungry
indicates that at least one extra zero must be added. */
struct poly_int_full {};
struct poly_int_hungry {};
/* poly_int_fullness::type is poly_int_full when B is true and
poly_int_hungry when B is false. */
template struct poly_int_fullness;
template<> struct poly_int_fullness { using type = poly_int_hungry; };
template<> struct poly_int_fullness { using type = poly_int_full; };
/* A class containing polynomial integers. The polynomial has N coefficients
of type C, and N - 1 indeterminates. */
template
struct poly_int
{
public:
poly_int () = default;
poly_int (const poly_int &) = default;
template
poly_int (const poly_int &);
template
constexpr poly_int (const Cs &...);
poly_int &operator = (const poly_int &) = default;
template
poly_int &operator = (const poly_int &);
template
typename if_nonpoly::type &operator = (const Ca &);
template
poly_int &operator += (const poly_int &);
template
typename if_nonpoly::type &operator += (const Ca &);
template
poly_int &operator -= (const poly_int &);
template
typename if_nonpoly::type &operator -= (const Ca &);
template
typename if_nonpoly::type &operator *= (const Ca &);
poly_int &operator <<= (unsigned int);
bool is_constant () const;
template
typename if_lossless::type is_constant (T *) const;
C to_constant () const;
template
static poly_int from (const poly_int &, unsigned int,
signop);
template
static poly_int from (const poly_int &, signop);
bool to_shwi (poly_int *) const;
bool to_uhwi (poly_int *) const;
poly_int force_shwi () const;
poly_int force_uhwi () const;
#if POLY_INT_CONVERSION
operator C () const;
#endif
C coeffs[N];
private:
template
constexpr poly_int (poly_int_full, const Cs &...);
template
constexpr poly_int (poly_int_hungry, const C0 &, const Cs &...);
};
template
template
inline
poly_int::poly_int (const poly_int &a)
{
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, *this, i, a.coeffs[i]);
}
template
template
inline constexpr
poly_int::poly_int (const Cs &... cs)
: poly_int (typename poly_int_fullness= N>::type (),
cs...) {}
/* Initialize with c0, cs..., and some trailing zeros. */
template
template
inline constexpr
poly_int::poly_int (poly_int_hungry, const C0 &c0, const Cs &... cs)
: poly_int (c0, cs..., wi::ints_for::zero (c0)) {}
/* Initialize with cs... directly, casting where necessary. */
template
template
inline constexpr
poly_int::poly_int (poly_int_full, const Cs &... cs)
: coeffs { (typename poly_coeff_traits::
template init_cast::type (cs))... } {}
template
template
inline poly_int&
poly_int::operator = (const poly_int &a)
{
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, *this, i, a.coeffs[i]);
return *this;
}
template
template
inline typename if_nonpoly >::type &
poly_int::operator = (const Ca &a)
{
POLY_SET_COEFF (C, *this, 0, a);
if (N >= 2)
for (unsigned int i = 1; i < N; i++)
POLY_SET_COEFF (C, *this, i, wi::ints_for::zero (this->coeffs[0]));
return *this;
}
template
template
inline poly_int&
poly_int::operator += (const poly_int &a)
{
for (unsigned int i = 0; i < N; i++)
this->coeffs[i] += a.coeffs[i];
return *this;
}
template
template
inline typename if_nonpoly >::type &
poly_int::operator += (const Ca &a)
{
this->coeffs[0] += a;
return *this;
}
template
template
inline poly_int&
poly_int::operator -= (const poly_int &a)
{
for (unsigned int i = 0; i < N; i++)
this->coeffs[i] -= a.coeffs[i];
return *this;
}
template
template
inline typename if_nonpoly >::type &
poly_int::operator -= (const Ca &a)
{
this->coeffs[0] -= a;
return *this;
}
template
template
inline typename if_nonpoly >::type &
poly_int::operator *= (const Ca &a)
{
for (unsigned int i = 0; i < N; i++)
this->coeffs[i] *= a;
return *this;
}
template
inline poly_int&
poly_int::operator <<= (unsigned int a)
{
for (unsigned int i = 0; i < N; i++)
this->coeffs[i] <<= a;
return *this;
}
/* Return true if the polynomial value is a compile-time constant. */
template
inline bool
poly_int::is_constant () const
{
if (N >= 2)
for (unsigned int i = 1; i < N; i++)
if (this->coeffs[i] != 0)
return false;
return true;
}
/* Return true if the polynomial value is a compile-time constant,
storing its value in CONST_VALUE if so. */
template
template
inline typename if_lossless::type
poly_int::is_constant (T *const_value) const
{
if (is_constant ())
{
*const_value = this->coeffs[0];
return true;
}
return false;
}
/* Return the value of a polynomial that is already known to be a
compile-time constant.
NOTE: When using this function, please add a comment above the call
explaining why we know the value is constant in that context. */
template
inline C
poly_int::to_constant () const
{
gcc_checking_assert (is_constant ());
return this->coeffs[0];
}
/* Convert X to a wide_int-based polynomial in which each coefficient
has BITSIZE bits. If X's coefficients are smaller than BITSIZE,
extend them according to SGN. */
template
template
inline poly_int
poly_int::from (const poly_int &a, unsigned int bitsize,
signop sgn)
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, C::from (a.coeffs[i], bitsize, sgn));
return r;
}
/* Convert X to a fixed_wide_int-based polynomial, extending according
to SGN. */
template
template
inline poly_int
poly_int::from (const poly_int &a, signop sgn)
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, C::from (a.coeffs[i], sgn));
return r;
}
/* Return true if the coefficients of this generic_wide_int-based
polynomial can be represented as signed HOST_WIDE_INTs without loss
of precision. Store the HOST_WIDE_INT representation in *R if so. */
template
inline bool
poly_int::to_shwi (poly_int *r) const
{
for (unsigned int i = 0; i < N; i++)
if (!wi::fits_shwi_p (this->coeffs[i]))
return false;
for (unsigned int i = 0; i < N; i++)
r->coeffs[i] = this->coeffs[i].to_shwi ();
return true;
}
/* Return true if the coefficients of this generic_wide_int-based
polynomial can be represented as unsigned HOST_WIDE_INTs without
loss of precision. Store the unsigned HOST_WIDE_INT representation
in *R if so. */
template
inline bool
poly_int::to_uhwi (poly_int *r) const
{
for (unsigned int i = 0; i < N; i++)
if (!wi::fits_uhwi_p (this->coeffs[i]))
return false;
for (unsigned int i = 0; i < N; i++)
r->coeffs[i] = this->coeffs[i].to_uhwi ();
return true;
}
/* Force a generic_wide_int-based constant to HOST_WIDE_INT precision,
truncating if necessary. */
template
inline poly_int
poly_int::force_shwi () const
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
r.coeffs[i] = this->coeffs[i].to_shwi ();
return r;
}
/* Force a generic_wide_int-based constant to unsigned HOST_WIDE_INT precision,
truncating if necessary. */
template
inline poly_int
poly_int::force_uhwi () const
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
r.coeffs[i] = this->coeffs[i].to_uhwi ();
return r;
}
#if POLY_INT_CONVERSION
/* Provide a conversion operator to constants. */
template
inline
poly_int::operator C () const
{
gcc_checking_assert (this->is_constant ());
return this->coeffs[0];
}
#endif
/* Return true if every coefficient of A is in the inclusive range [B, C]. */
template
inline typename if_nonpoly::type
coeffs_in_range_p (const Ca &a, const Cb &b, const Cc &c)
{
return a >= b && a <= c;
}
template
inline typename if_nonpoly::type
coeffs_in_range_p (const poly_int &a, const Cb &b, const Cc &c)
{
for (unsigned int i = 0; i < N; i++)
if (a.coeffs[i] < b || a.coeffs[i] > c)
return false;
return true;
}
namespace wi {
/* Poly version of wi::shwi, with the same interface. */
template
inline poly_int
shwi (const poly_int &a, unsigned int precision)
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (hwi_with_prec, r, i, wi::shwi (a.coeffs[i], precision));
return r;
}
/* Poly version of wi::uhwi, with the same interface. */
template
inline poly_int
uhwi (const poly_int &a, unsigned int precision)
{
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (hwi_with_prec, r, i, wi::uhwi (a.coeffs[i], precision));
return r;
}
/* Poly version of wi::sext, with the same interface. */
template
inline POLY_POLY_RESULT (N, Ca, Ca)
sext (const poly_int &a, unsigned int precision)
{
typedef POLY_POLY_COEFF (Ca, Ca) C;
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, wi::sext (a.coeffs[i], precision));
return r;
}
/* Poly version of wi::zext, with the same interface. */
template
inline POLY_POLY_RESULT (N, Ca, Ca)
zext (const poly_int &a, unsigned int precision)
{
typedef POLY_POLY_COEFF (Ca, Ca) C;
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, wi::zext (a.coeffs[i], precision));
return r;
}
}
template
inline POLY_POLY_RESULT (N, Ca, Cb)
operator + (const poly_int &a, const poly_int &b)
{
typedef POLY_CAST (Ca, Cb) NCa;
typedef POLY_POLY_COEFF (Ca, Cb) C;
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, NCa (a.coeffs[i]) + b.coeffs[i]);
return r;
}
template
inline POLY_CONST_RESULT (N, Ca, Cb)
operator + (const poly_int &a, const Cb &b)
{
typedef POLY_CAST (Ca, Cb) NCa;
typedef POLY_CONST_COEFF (Ca, Cb) C;
poly_int r;
POLY_SET_COEFF (C, r, 0, NCa (a.coeffs[0]) + b);
if (N >= 2)
for (unsigned int i = 1; i < N; i++)
POLY_SET_COEFF (C, r, i, NCa (a.coeffs[i]));
return r;
}
template
inline CONST_POLY_RESULT (N, Ca, Cb)
operator + (const Ca &a, const poly_int &b)
{
typedef POLY_CAST (Cb, Ca) NCb;
typedef CONST_POLY_COEFF (Ca, Cb) C;
poly_int r;
POLY_SET_COEFF (C, r, 0, a + NCb (b.coeffs[0]));
if (N >= 2)
for (unsigned int i = 1; i < N; i++)
POLY_SET_COEFF (C, r, i, NCb (b.coeffs[i]));
return r;
}
namespace wi {
/* Poly versions of wi::add, with the same interface. */
template
inline poly_int
add (const poly_int &a, const poly_int &b)
{
typedef WI_BINARY_RESULT (Ca, Cb) C;
poly_int r;
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, r, i, wi::add (a.coeffs[i], b.coeffs[i]));
return r;
}
template
inline poly_int
add (const poly_int &a, const Cb &b)
{
typedef WI_BINARY_RESULT (Ca, Cb) C;
poly_int r;
POLY_SET_COEFF (C, r, 0, wi::add (a.coeffs[0], b));
for (unsigned int i = 1; i < N; i++)
POLY_SET_COEFF (C, r, i, wi::add (a.coeffs[i],
wi::ints_for::zero (b)));
return r;
}
template
inline poly_int
add (const Ca &a, const poly_int &b)
{
typedef WI_BINARY_RESULT (Ca, Cb) C;
poly_int