/* Polynomial integer classes.
Copyright (C) 2014-2023 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 struct poly_int_pod;
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. */
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 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 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;
};
/* 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;
};
template
struct poly_int_traits > : poly_int_traits >
{
};
/* 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)))
/* A base POD class for polynomial integers. The polynomial has N
coefficients of type C. */
template
struct poly_int_pod
{
public:
template
poly_int_pod &operator = (const poly_int_pod &);
template
typename if_nonpoly::type &operator = (const Ca &);
template
poly_int_pod &operator += (const poly_int_pod &);
template
typename if_nonpoly::type &operator += (const Ca &);
template
poly_int_pod &operator -= (const poly_int_pod &);
template
typename if_nonpoly::type &operator -= (const Ca &);
template
typename if_nonpoly::type &operator *= (const Ca &);
poly_int_pod &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_pod &, unsigned int,
signop);
template
static poly_int from (const poly_int_pod &, signop);
bool to_shwi (poly_int_pod *) const;
bool to_uhwi (poly_int_pod *) const;
poly_int force_shwi () const;
poly_int force_uhwi () const;
#if POLY_INT_CONVERSION
operator C () const;
#endif
C coeffs[N];
};
template
template
inline poly_int_pod&
poly_int_pod::operator = (const poly_int_pod &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_pod::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_pod&
poly_int_pod::operator += (const poly_int_pod &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_pod::operator += (const Ca &a)
{
this->coeffs[0] += a;
return *this;
}
template
template
inline poly_int_pod&
poly_int_pod::operator -= (const poly_int_pod &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_pod::operator -= (const Ca &a)
{
this->coeffs[0] -= a;
return *this;
}
template
template
inline typename if_nonpoly >::type &
poly_int_pod::operator *= (const Ca &a)
{
for (unsigned int i = 0; i < N; i++)
this->coeffs[i] *= a;
return *this;
}
template
inline poly_int_pod&
poly_int_pod::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_pod::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_pod::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_pod::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_pod::from (const poly_int_pod &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_pod::from (const poly_int_pod &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_pod::to_shwi (poly_int_pod *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_pod::to_uhwi (poly_int_pod *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_pod::force_shwi () const
{
poly_int_pod 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_pod::force_uhwi () const
{
poly_int_pod 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_pod::operator C () const
{
gcc_checking_assert (this->is_constant ());
return this->coeffs[0];
}
#endif
/* The main class for polynomial integers. The class provides
constructors that are necessarily missing from the POD base. */
template
class poly_int : public poly_int_pod
{
public:
poly_int () {}
template
poly_int (const poly_int &);
template
poly_int (const poly_int_pod &);
template
poly_int (const C0 &);
template
poly_int (const C0 &, const C1 &);
template
poly_int &operator = (const poly_int_pod &);
template
typename if_nonpoly::type &operator = (const Ca &);
template
poly_int &operator += (const poly_int_pod &);
template
typename if_nonpoly::type &operator += (const Ca &);
template
poly_int &operator -= (const poly_int_pod &);
template
typename if_nonpoly::type &operator -= (const Ca &);
template
typename if_nonpoly::type &operator *= (const Ca &);
poly_int &operator <<= (unsigned int);
};
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
poly_int::poly_int (const poly_int_pod &a)
{
for (unsigned int i = 0; i < N; i++)
POLY_SET_COEFF (C, *this, i, a.coeffs[i]);
}
template
template
inline
poly_int::poly_int (const C0 &c0)
{
POLY_SET_COEFF (C, *this, 0, c0);
for (unsigned int i = 1; i < N; i++)
POLY_SET_COEFF (C, *this, i, wi::ints_for::zero (this->coeffs[0]));
}
template
template
inline
poly_int::poly_int (const C0 &c0, const C1 &c1)
{
STATIC_ASSERT (N >= 2);
POLY_SET_COEFF (C, *this, 0, c0);
POLY_SET_COEFF (C, *this, 1, c1);
for (unsigned int i = 2; i < N; i++)
POLY_SET_COEFF (C, *this, i, wi::ints_for::zero (this->coeffs[0]));
}
template
template
inline poly_int&
poly_int::operator = (const poly_int_pod &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;
if (N >= 2)
for (unsigned int i = 1; i < N; i++)
this->coeffs[i] = wi::ints_for::zero (this->coeffs[0]);
return *this;
}
template
template
inline poly_int&
poly_int::operator += (const poly_int_pod &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_pod &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 every coefficient of A is in the inclusive range [B, C]. */
template