/* A class for building vector constant patterns. | |

Copyright (C) 2017-2022 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 | |

<http://www.gnu.org/licenses/>. */ | |

#ifndef GCC_VECTOR_BUILDER_H | |

#define GCC_VECTOR_BUILDER_H | |

/* This class is a wrapper around auto_vec<T> for building vectors of T. | |

It aims to encode each vector as npatterns interleaved patterns, | |

where each pattern represents a sequence: | |

{ BASE0, BASE1, BASE1 + STEP, BASE1 + STEP*2, BASE1 + STEP*3, ... } | |

The first three elements in each pattern provide enough information | |

to derive the other elements. If all patterns have a STEP of zero, | |

we only need to encode the first two elements in each pattern. | |

If BASE1 is also equal to BASE0 for all patterns, we only need to | |

encode the first element in each pattern. The number of encoded | |

elements per pattern is given by nelts_per_pattern. | |

The class can be used in two ways: | |

1. It can be used to build a full image of the vector, which is then | |

canonicalized by finalize (). In this case npatterns is initially | |

the number of elements in the vector and nelts_per_pattern is | |

initially 1. | |

2. It can be used to build a vector that already has a known encoding. | |

This is preferred since it is more efficient and copes with | |

variable-length vectors. finalize () then canonicalizes the encoding | |

to a simpler form if possible. | |

Shape is the type that specifies the number of elements in the vector | |

and (where relevant) the type of each element. | |

The derived class Derived provides the functionality of this class | |

for specific Ts. Derived needs to provide the following interface: | |

bool equal_p (T elt1, T elt2) const; | |

Return true if elements ELT1 and ELT2 are equal. | |

bool allow_steps_p () const; | |

Return true if a stepped representation is OK. We don't allow | |

linear series for anything other than integers, to avoid problems | |

with rounding. | |

bool integral_p (T elt) const; | |

Return true if element ELT can be interpreted as an integer. | |

StepType step (T elt1, T elt2) const; | |

Return the value of element ELT2 minus the value of element ELT1, | |

given integral_p (ELT1) && integral_p (ELT2). There is no fixed | |

choice of StepType. | |

T apply_step (T base, unsigned int factor, StepType step) const; | |

Return a vector element with the value BASE + FACTOR * STEP. | |

bool can_elide_p (T elt) const; | |

Return true if we can drop element ELT, even if the retained | |

elements are different. This is provided for TREE_OVERFLOW | |

handling. | |

void note_representative (T *elt1_ptr, T elt2); | |

Record that ELT2 is being elided, given that ELT1_PTR points to | |

the last encoded element for the containing pattern. This is | |

again provided for TREE_OVERFLOW handling. | |

static poly_uint64 shape_nelts (Shape shape); | |

Return the number of elements in SHAPE. | |

The class provides additional functionality for the case in which | |

T can describe a vector constant as well as an individual element. | |

This functionality requires: | |

static poly_uint64 nelts_of (T x); | |

Return the number of elements in vector constant X. | |

static unsigned int npatterns_of (T x); | |

Return the number of patterns used to encode vector constant X. | |

static unsigned int nelts_per_pattern_of (T x); | |

Return the number of elements used to encode each pattern | |

in vector constant X. */ | |

template<typename T, typename Shape, typename Derived> | |

class vector_builder : public auto_vec<T, 32> | |

{ | |

public: | |

vector_builder (); | |

poly_uint64 full_nelts () const { return m_full_nelts; } | |

unsigned int npatterns () const { return m_npatterns; } | |

unsigned int nelts_per_pattern () const { return m_nelts_per_pattern; } | |

unsigned int encoded_nelts () const; | |

bool encoded_full_vector_p () const; | |

T elt (unsigned int) const; | |

unsigned int count_dups (int, int, int) const; | |

bool operator == (const Derived &) const; | |

bool operator != (const Derived &x) const { return !operator == (x); } | |

bool new_unary_operation (Shape, T, bool); | |

bool new_binary_operation (Shape, T, T, bool); | |

void finalize (); | |

static unsigned int binary_encoded_nelts (T, T); | |

protected: | |

void new_vector (poly_uint64, unsigned int, unsigned int); | |

void reshape (unsigned int, unsigned int); | |

bool repeating_sequence_p (unsigned int, unsigned int, unsigned int); | |

bool stepped_sequence_p (unsigned int, unsigned int, unsigned int); | |

bool try_npatterns (unsigned int); | |

private: | |

vector_builder (const vector_builder &); | |

vector_builder &operator= (const vector_builder &); | |

Derived *derived () { return static_cast<Derived *> (this); } | |

const Derived *derived () const; | |

poly_uint64 m_full_nelts; | |

unsigned int m_npatterns; | |

unsigned int m_nelts_per_pattern; | |

}; | |

template<typename T, typename Shape, typename Derived> | |

inline const Derived * | |

vector_builder<T, Shape, Derived>::derived () const | |

{ | |

return static_cast<const Derived *> (this); | |

} | |

template<typename T, typename Shape, typename Derived> | |

inline | |

vector_builder<T, Shape, Derived>::vector_builder () | |

: m_full_nelts (0), | |

m_npatterns (0), | |

m_nelts_per_pattern (0) | |

{} | |

/* Return the number of elements that are explicitly encoded. The vec | |

starts with these explicitly-encoded elements and may contain additional | |

elided elements. */ | |

template<typename T, typename Shape, typename Derived> | |

inline unsigned int | |

vector_builder<T, Shape, Derived>::encoded_nelts () const | |

{ | |

return m_npatterns * m_nelts_per_pattern; | |

} | |

/* Return true if every element of the vector is explicitly encoded. */ | |

template<typename T, typename Shape, typename Derived> | |

inline bool | |

vector_builder<T, Shape, Derived>::encoded_full_vector_p () const | |

{ | |

return known_eq (m_npatterns * m_nelts_per_pattern, m_full_nelts); | |

} | |

/* Start building a vector that has FULL_NELTS elements. Initially | |

encode it using NPATTERNS patterns with NELTS_PER_PATTERN each. */ | |

template<typename T, typename Shape, typename Derived> | |

void | |

vector_builder<T, Shape, Derived>::new_vector (poly_uint64 full_nelts, | |

unsigned int npatterns, | |

unsigned int nelts_per_pattern) | |

{ | |

m_full_nelts = full_nelts; | |

m_npatterns = npatterns; | |

m_nelts_per_pattern = nelts_per_pattern; | |

this->reserve (encoded_nelts ()); | |

this->truncate (0); | |

} | |

/* Return true if this vector and OTHER have the same elements and | |

are encoded in the same way. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::operator == (const Derived &other) const | |

{ | |

if (maybe_ne (m_full_nelts, other.m_full_nelts) | |

|| m_npatterns != other.m_npatterns | |

|| m_nelts_per_pattern != other.m_nelts_per_pattern) | |

return false; | |

unsigned int nelts = encoded_nelts (); | |

for (unsigned int i = 0; i < nelts; ++i) | |

if (!derived ()->equal_p ((*this)[i], other[i])) | |

return false; | |

return true; | |

} | |

/* Return the value of vector element I, which might or might not be | |

encoded explicitly. */ | |

template<typename T, typename Shape, typename Derived> | |

T | |

vector_builder<T, Shape, Derived>::elt (unsigned int i) const | |

{ | |

/* First handle elements that are already present in the underlying | |

vector, regardless of whether they're part of the encoding or not. */ | |

if (i < this->length ()) | |

return (*this)[i]; | |

/* Extrapolation is only possible if the encoding has been fully | |

populated. */ | |

gcc_checking_assert (encoded_nelts () <= this->length ()); | |

/* Identify the pattern that contains element I and work out the index of | |

the last encoded element for that pattern. */ | |

unsigned int pattern = i % m_npatterns; | |

unsigned int count = i / m_npatterns; | |

unsigned int final_i = encoded_nelts () - m_npatterns + pattern; | |

T final = (*this)[final_i]; | |

/* If there are no steps, the final encoded value is the right one. */ | |

if (m_nelts_per_pattern <= 2) | |

return final; | |

/* Otherwise work out the value from the last two encoded elements. */ | |

T prev = (*this)[final_i - m_npatterns]; | |

return derived ()->apply_step (final, count - 2, | |

derived ()->step (prev, final)); | |

} | |

/* Try to start building a new vector of shape SHAPE that holds the result of | |

a unary operation on vector constant VEC. ALLOW_STEPPED_P is true if the | |

operation can handle stepped encodings directly, without having to expand | |

the full sequence. | |

Return true if the operation is possible, which it always is when | |

ALLOW_STEPPED_P is true. Leave the builder unchanged otherwise. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::new_unary_operation (Shape shape, T vec, | |

bool allow_stepped_p) | |

{ | |

poly_uint64 full_nelts = Derived::shape_nelts (shape); | |

gcc_assert (known_eq (full_nelts, Derived::nelts_of (vec))); | |

unsigned int npatterns = Derived::npatterns_of (vec); | |

unsigned int nelts_per_pattern = Derived::nelts_per_pattern_of (vec); | |

if (!allow_stepped_p && nelts_per_pattern > 2) | |

{ | |

if (!full_nelts.is_constant ()) | |

return false; | |

npatterns = full_nelts.to_constant (); | |

nelts_per_pattern = 1; | |

} | |

derived ()->new_vector (shape, npatterns, nelts_per_pattern); | |

return true; | |

} | |

/* Try to start building a new vector of shape SHAPE that holds the result of | |

a binary operation on vector constants VEC1 and VEC2. ALLOW_STEPPED_P is | |

true if the operation can handle stepped encodings directly, without | |

having to expand the full sequence. | |

Return true if the operation is possible. Leave the builder unchanged | |

otherwise. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::new_binary_operation (Shape shape, | |

T vec1, T vec2, | |

bool allow_stepped_p) | |

{ | |

poly_uint64 full_nelts = Derived::shape_nelts (shape); | |

gcc_assert (known_eq (full_nelts, Derived::nelts_of (vec1)) | |

&& known_eq (full_nelts, Derived::nelts_of (vec2))); | |

/* Conceptually we split the patterns in VEC1 and VEC2 until we have | |

an equal number for both. Each split pattern requires the same | |

number of elements per pattern as the original. E.g. splitting: | |

{ 1, 2, 3, ... } | |

into two gives: | |

{ 1, 3, 5, ... } | |

{ 2, 4, 6, ... } | |

while splitting: | |

{ 1, 0, ... } | |

into two gives: | |

{ 1, 0, ... } | |

{ 0, 0, ... }. */ | |

unsigned int npatterns | |

= least_common_multiple (Derived::npatterns_of (vec1), | |

Derived::npatterns_of (vec2)); | |

unsigned int nelts_per_pattern | |

= MAX (Derived::nelts_per_pattern_of (vec1), | |

Derived::nelts_per_pattern_of (vec2)); | |

if (!allow_stepped_p && nelts_per_pattern > 2) | |

{ | |

if (!full_nelts.is_constant ()) | |

return false; | |

npatterns = full_nelts.to_constant (); | |

nelts_per_pattern = 1; | |

} | |

derived ()->new_vector (shape, npatterns, nelts_per_pattern); | |

return true; | |

} | |

/* Return the number of elements that the caller needs to operate on in | |

order to handle a binary operation on vector constants VEC1 and VEC2. | |

This static function is used instead of new_binary_operation if the | |

result of the operation is not a constant vector. */ | |

template<typename T, typename Shape, typename Derived> | |

unsigned int | |

vector_builder<T, Shape, Derived>::binary_encoded_nelts (T vec1, T vec2) | |

{ | |

poly_uint64 nelts = Derived::nelts_of (vec1); | |

gcc_assert (known_eq (nelts, Derived::nelts_of (vec2))); | |

/* See new_binary_operation for details. */ | |

unsigned int npatterns | |

= least_common_multiple (Derived::npatterns_of (vec1), | |

Derived::npatterns_of (vec2)); | |

unsigned int nelts_per_pattern | |

= MAX (Derived::nelts_per_pattern_of (vec1), | |

Derived::nelts_per_pattern_of (vec2)); | |

unsigned HOST_WIDE_INT const_nelts; | |

if (nelts.is_constant (&const_nelts)) | |

return MIN (npatterns * nelts_per_pattern, const_nelts); | |

return npatterns * nelts_per_pattern; | |

} | |

/* Return the number of leading duplicate elements in the range | |

[START:END:STEP]. The value is always at least 1. */ | |

template<typename T, typename Shape, typename Derived> | |

unsigned int | |

vector_builder<T, Shape, Derived>::count_dups (int start, int end, | |

int step) const | |

{ | |

gcc_assert ((end - start) % step == 0); | |

unsigned int ndups = 1; | |

for (int i = start + step; | |

i != end && derived ()->equal_p (elt (i), elt (start)); | |

i += step) | |

ndups++; | |

return ndups; | |

} | |

/* Change the encoding to NPATTERNS patterns of NELTS_PER_PATTERN each, | |

but without changing the underlying vector. */ | |

template<typename T, typename Shape, typename Derived> | |

void | |

vector_builder<T, Shape, Derived>::reshape (unsigned int npatterns, | |

unsigned int nelts_per_pattern) | |

{ | |

unsigned int old_encoded_nelts = encoded_nelts (); | |

unsigned int new_encoded_nelts = npatterns * nelts_per_pattern; | |

gcc_checking_assert (new_encoded_nelts <= old_encoded_nelts); | |

unsigned int next = new_encoded_nelts - npatterns; | |

for (unsigned int i = new_encoded_nelts; i < old_encoded_nelts; ++i) | |

{ | |

derived ()->note_representative (&(*this)[next], (*this)[i]); | |

next += 1; | |

if (next == new_encoded_nelts) | |

next -= npatterns; | |

} | |

m_npatterns = npatterns; | |

m_nelts_per_pattern = nelts_per_pattern; | |

} | |

/* Return true if elements [START, END) contain a repeating sequence of | |

STEP elements. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::repeating_sequence_p (unsigned int start, | |

unsigned int end, | |

unsigned int step) | |

{ | |

for (unsigned int i = start; i < end - step; ++i) | |

if (!derived ()->equal_p ((*this)[i], (*this)[i + step])) | |

return false; | |

return true; | |

} | |

/* Return true if elements [START, END) contain STEP interleaved linear | |

series. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::stepped_sequence_p (unsigned int start, | |

unsigned int end, | |

unsigned int step) | |

{ | |

if (!derived ()->allow_steps_p ()) | |

return false; | |

for (unsigned int i = start + step * 2; i < end; ++i) | |

{ | |

T elt1 = (*this)[i - step * 2]; | |

T elt2 = (*this)[i - step]; | |

T elt3 = (*this)[i]; | |

if (!derived ()->integral_p (elt1) | |

|| !derived ()->integral_p (elt2) | |

|| !derived ()->integral_p (elt3)) | |

return false; | |

if (maybe_ne (derived ()->step (elt1, elt2), | |

derived ()->step (elt2, elt3))) | |

return false; | |

if (!derived ()->can_elide_p (elt3)) | |

return false; | |

} | |

return true; | |

} | |

/* Try to change the number of encoded patterns to NPATTERNS, returning | |

true on success. */ | |

template<typename T, typename Shape, typename Derived> | |

bool | |

vector_builder<T, Shape, Derived>::try_npatterns (unsigned int npatterns) | |

{ | |

if (m_nelts_per_pattern == 1) | |

{ | |

/* See whether NPATTERNS is valid with the current 1-element-per-pattern | |

encoding. */ | |

if (repeating_sequence_p (0, encoded_nelts (), npatterns)) | |

{ | |

reshape (npatterns, 1); | |

return true; | |

} | |

/* We can only increase the number of elements per pattern if all | |

elements are still encoded explicitly. */ | |

if (!encoded_full_vector_p ()) | |

return false; | |

} | |

if (m_nelts_per_pattern <= 2) | |

{ | |

/* See whether NPATTERNS is valid with a 2-element-per-pattern | |

encoding. */ | |

if (repeating_sequence_p (npatterns, encoded_nelts (), npatterns)) | |

{ | |

reshape (npatterns, 2); | |

return true; | |

} | |

/* We can only increase the number of elements per pattern if all | |

elements are still encoded explicitly. */ | |

if (!encoded_full_vector_p ()) | |

return false; | |

} | |

if (m_nelts_per_pattern <= 3) | |

{ | |

/* See whether we have NPATTERNS interleaved linear series, | |

giving a 3-element-per-pattern encoding. */ | |

if (stepped_sequence_p (npatterns, encoded_nelts (), npatterns)) | |

{ | |

reshape (npatterns, 3); | |

return true; | |

} | |

return false; | |

} | |

gcc_unreachable (); | |

} | |

/* Replace the current encoding with the canonical form. */ | |

template<typename T, typename Shape, typename Derived> | |

void | |

vector_builder<T, Shape, Derived>::finalize () | |

{ | |

/* The encoding requires the same number of elements to come from each | |

pattern. */ | |

gcc_assert (multiple_p (m_full_nelts, m_npatterns)); | |

/* Allow the caller to build more elements than necessary. For example, | |

it's often convenient to build a stepped vector from the natural | |

encoding of three elements even if the vector itself only has two. */ | |

unsigned HOST_WIDE_INT const_full_nelts; | |

if (m_full_nelts.is_constant (&const_full_nelts) | |

&& const_full_nelts <= encoded_nelts ()) | |

{ | |

m_npatterns = const_full_nelts; | |

m_nelts_per_pattern = 1; | |

} | |

/* Try to whittle down the number of elements per pattern. That is: | |

1. If we have stepped patterns whose steps are all 0, reduce the | |

number of elements per pattern from 3 to 2. | |

2. If we have background fill values that are the same as the | |

foreground values, reduce the number of elements per pattern | |

from 2 to 1. */ | |

while (m_nelts_per_pattern > 1 | |

&& repeating_sequence_p (encoded_nelts () - m_npatterns * 2, | |

encoded_nelts (), m_npatterns)) | |

/* The last two sequences of M_NPATTERNS elements are equal, | |

so remove the last one. */ | |

reshape (m_npatterns, m_nelts_per_pattern - 1); | |

if (pow2p_hwi (m_npatterns)) | |

{ | |

/* Try to halve the number of patterns while doing so gives a | |

valid pattern. This approach is linear in the number of | |

elements, whereas searcing from 1 up would be O(n*log(n)). | |

Each halving step tries to keep the number of elements per pattern | |

the same. If that isn't possible, and if all elements are still | |

explicitly encoded, the halving step can instead increase the number | |

of elements per pattern. | |

E.g. for: | |

{ 0, 2, 3, 4, 5, 6, 7, 8 } npatterns == 8 full_nelts == 8 | |

we first realize that the second half of the sequence is not | |

equal to the first, so we cannot maintain 1 element per pattern | |

for npatterns == 4. Instead we halve the number of patterns | |

and double the number of elements per pattern, treating this | |

as a "foreground" { 0, 2, 3, 4 } against a "background" of | |

{ 5, 6, 7, 8 | 5, 6, 7, 8 ... }: | |

{ 0, 2, 3, 4 | 5, 6, 7, 8 } npatterns == 4 | |

Next we realize that this is *not* a foreround of { 0, 2 } | |

against a background of { 3, 4 | 3, 4 ... }, so the only | |

remaining option for reducing the number of patterns is | |

to use a foreground of { 0, 2 } against a stepped background | |

of { 1, 2 | 3, 4 | 5, 6 ... }. This is valid because we still | |

haven't elided any elements: | |

{ 0, 2 | 3, 4 | 5, 6 } npatterns == 2 | |

This in turn can be reduced to a foreground of { 0 } against a | |

stepped background of { 1 | 2 | 3 ... }: | |

{ 0 | 2 | 3 } npatterns == 1 | |

This last step would not have been possible for: | |

{ 0, 0 | 3, 4 | 5, 6 } npatterns == 2. */ | |

while ((m_npatterns & 1) == 0 && try_npatterns (m_npatterns / 2)) | |

continue; | |

/* Builders of arbitrary fixed-length vectors can use: | |

new_vector (x, x, 1) | |

so that every element is specified explicitly. Handle cases | |

that are actually wrapping series, like { 0, 1, 2, 3, 0, 1, 2, 3 } | |

would be for 2-bit elements. We'll have treated them as | |

duplicates in the loop above. */ | |

if (m_nelts_per_pattern == 1 | |

&& m_full_nelts.is_constant (&const_full_nelts) | |

&& this->length () >= const_full_nelts | |

&& (m_npatterns & 3) == 0 | |

&& stepped_sequence_p (m_npatterns / 4, const_full_nelts, | |

m_npatterns / 4)) | |

{ | |

reshape (m_npatterns / 4, 3); | |

while ((m_npatterns & 1) == 0 && try_npatterns (m_npatterns / 2)) | |

continue; | |

} | |

} | |

else | |

/* For the non-power-of-2 case, do a simple search up from 1. */ | |

for (unsigned int i = 1; i <= m_npatterns / 2; ++i) | |

if (m_npatterns % i == 0 && try_npatterns (i)) | |

break; | |

} | |

#endif |