blob: 47fb90d21c0bae307cb26b3c9f53a53a929dde3b [file] [log] [blame]
//===- TypeSize.h - Wrapper around type sizes -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides a struct that can be used to query the size of IR types
// which may be scalable vectors. It provides convenience operators so that
// it can be used in much the same way as a single scalar value.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_TYPESIZE_H
#define LLVM_SUPPORT_TYPESIZE_H
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/WithColor.h"
#include <cstdint>
#include <cassert>
namespace llvm {
template <typename T> struct DenseMapInfo;
// TODO: This class will be redesigned in a later patch that introduces full
// polynomial behaviour, i.e. the ability to have composites made up of both
// fixed and scalable sizes.
template <typename T> class PolySize {
protected:
T MinVal; // The minimum value that it could be.
bool IsScalable; // If true, the total value is determined by multiplying
// 'MinVal' by a runtime determinded quantity, 'vscale'.
constexpr PolySize(T MinVal, bool IsScalable)
: MinVal(MinVal), IsScalable(IsScalable) {}
public:
static constexpr PolySize getFixed(T MinVal) { return {MinVal, false}; }
static constexpr PolySize getScalable(T MinVal) { return {MinVal, true}; }
static constexpr PolySize get(T MinVal, bool IsScalable) {
return {MinVal, IsScalable};
}
static constexpr PolySize getNull() { return {0, false}; }
/// Counting predicates.
///
///@{ No elements..
bool isZero() const { return MinVal == 0; }
/// At least one element.
bool isNonZero() const { return !isZero(); }
/// A return value of true indicates we know at compile time that the number
/// of elements (vscale * Min) is definitely even. However, returning false
/// does not guarantee that the total number of elements is odd.
bool isKnownEven() const { return (MinVal & 0x1) == 0; }
///@}
T getKnownMinValue() const { return MinVal; }
// Return the minimum value with the assumption that the count is exact.
// Use in places where a scalable count doesn't make sense (e.g. non-vector
// types, or vectors in backends which don't support scalable vectors).
T getFixedValue() const {
assert(!IsScalable &&
"Request for a fixed element count on a scalable object");
return MinVal;
}
bool isScalable() const { return IsScalable; }
bool operator==(const PolySize &RHS) const {
return MinVal == RHS.MinVal && IsScalable == RHS.IsScalable;
}
bool operator!=(const PolySize &RHS) const { return !(*this == RHS); }
// For some cases, size ordering between scalable and fixed size types cannot
// be determined at compile time, so such comparisons aren't allowed.
//
// e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
// vscale >= 5, equal sized with a vscale of 4, and smaller with
// a vscale <= 3.
//
// All the functions below make use of the fact vscale is always >= 1, which
// means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc.
static bool isKnownLT(const PolySize &LHS, const PolySize &RHS) {
if (!LHS.IsScalable || RHS.IsScalable)
return LHS.MinVal < RHS.MinVal;
// LHS.IsScalable = true, RHS.IsScalable = false
return false;
}
static bool isKnownGT(const PolySize &LHS, const PolySize &RHS) {
if (LHS.IsScalable || !RHS.IsScalable)
return LHS.MinVal > RHS.MinVal;
// LHS.IsScalable = false, RHS.IsScalable = true
return false;
}
static bool isKnownLE(const PolySize &LHS, const PolySize &RHS) {
if (!LHS.IsScalable || RHS.IsScalable)
return LHS.MinVal <= RHS.MinVal;
// LHS.IsScalable = true, RHS.IsScalable = false
return false;
}
static bool isKnownGE(const PolySize &LHS, const PolySize &RHS) {
if (LHS.IsScalable || !RHS.IsScalable)
return LHS.MinVal >= RHS.MinVal;
// LHS.IsScalable = false, RHS.IsScalable = true
return false;
}
PolySize operator*(T RHS) { return {MinVal * RHS, IsScalable}; }
PolySize &operator*=(T RHS) {
MinVal *= RHS;
return *this;
}
friend PolySize operator-(const PolySize &LHS, const PolySize &RHS) {
assert(LHS.IsScalable == RHS.IsScalable &&
"Arithmetic using mixed scalable and fixed types");
return {LHS.MinVal - RHS.MinVal, LHS.IsScalable};
}
/// This function tells the caller whether the element count is known at
/// compile time to be a multiple of the scalar value RHS.
bool isKnownMultipleOf(T RHS) const { return MinVal % RHS == 0; }
/// We do not provide the '/' operator here because division for polynomial
/// types does not work in the same way as for normal integer types. We can
/// only divide the minimum value (or coefficient) by RHS, which is not the
/// same as
/// (Min * Vscale) / RHS
/// The caller is recommended to use this function in combination with
/// isKnownMultipleOf(RHS), which lets the caller know if it's possible to
/// perform a lossless divide by RHS.
PolySize divideCoefficientBy(T RHS) const {
return PolySize(MinVal / RHS, IsScalable);
}
PolySize coefficientNextPowerOf2() const {
return PolySize(static_cast<T>(llvm::NextPowerOf2(MinVal)), IsScalable);
}
/// Printing function.
void print(raw_ostream &OS) const {
if (IsScalable)
OS << "vscale x ";
OS << MinVal;
}
};
/// Stream operator function for `PolySize`.
template <typename T>
inline raw_ostream &operator<<(raw_ostream &OS, const PolySize<T> &PS) {
PS.print(OS);
return OS;
}
class ElementCount : public PolySize<unsigned> {
public:
constexpr ElementCount(PolySize<unsigned> V) : PolySize(V) {}
/// Counting predicates.
///
/// Notice that MinVal = 1 and IsScalable = true is considered more than
/// one element.
///
///@{ No elements..
/// Exactly one element.
bool isScalar() const { return !IsScalable && MinVal == 1; }
/// One or more elements.
bool isVector() const { return (IsScalable && MinVal != 0) || MinVal > 1; }
///@}
};
// This class is used to represent the size of types. If the type is of fixed
// size, it will represent the exact size. If the type is a scalable vector,
// it will represent the known minimum size.
class TypeSize : public PolySize<uint64_t> {
public:
constexpr TypeSize(PolySize<uint64_t> V) : PolySize(V) {}
constexpr TypeSize(uint64_t MinVal, bool IsScalable)
: PolySize(MinVal, IsScalable) {}
static constexpr TypeSize Fixed(uint64_t MinVal) {
return TypeSize(MinVal, false);
}
static constexpr TypeSize Scalable(uint64_t MinVal) {
return TypeSize(MinVal, true);
}
uint64_t getFixedSize() const { return getFixedValue(); }
uint64_t getKnownMinSize() const { return getKnownMinValue(); }
friend bool operator<(const TypeSize &LHS, const TypeSize &RHS) {
assert(LHS.IsScalable == RHS.IsScalable &&
"Ordering comparison of scalable and fixed types");
return LHS.MinVal < RHS.MinVal;
}
friend bool operator>(const TypeSize &LHS, const TypeSize &RHS) {
return RHS < LHS;
}
friend bool operator<=(const TypeSize &LHS, const TypeSize &RHS) {
return !(RHS < LHS);
}
friend bool operator>=(const TypeSize &LHS, const TypeSize& RHS) {
return !(LHS < RHS);
}
TypeSize &operator-=(TypeSize RHS) {
assert(IsScalable == RHS.IsScalable &&
"Subtraction using mixed scalable and fixed types");
MinVal -= RHS.MinVal;
return *this;
}
TypeSize &operator+=(TypeSize RHS) {
assert(IsScalable == RHS.IsScalable &&
"Addition using mixed scalable and fixed types");
MinVal += RHS.MinVal;
return *this;
}
friend TypeSize operator-(const TypeSize &LHS, const TypeSize &RHS) {
assert(LHS.IsScalable == RHS.IsScalable &&
"Arithmetic using mixed scalable and fixed types");
return {LHS.MinVal - RHS.MinVal, LHS.IsScalable};
}
// Casts to a uint64_t if this is a fixed-width size.
//
// This interface is deprecated and will be removed in a future version
// of LLVM in favour of upgrading uses that rely on this implicit conversion
// to uint64_t. Calls to functions that return a TypeSize should use the
// proper interfaces to TypeSize.
// In practice this is mostly calls to MVT/EVT::getSizeInBits().
//
// To determine how to upgrade the code:
//
// if (<algorithm works for both scalable and fixed-width vectors>)
// use getKnownMinValue()
// else if (<algorithm works only for fixed-width vectors>) {
// if <algorithm can be adapted for both scalable and fixed-width vectors>
// update the algorithm and use getKnownMinValue()
// else
// bail out early for scalable vectors and use getFixedValue()
// }
operator uint64_t() const {
#ifdef STRICT_FIXED_SIZE_VECTORS
return getFixedValue();
#else
if (isScalable())
WithColor::warning() << "Compiler has made implicit assumption that "
"TypeSize is not scalable. This may or may not "
"lead to broken code.\n";
return getKnownMinValue();
#endif
}
// Convenience operators to obtain relative sizes independently of
// the scalable flag.
TypeSize operator*(unsigned RHS) const { return {MinVal * RHS, IsScalable}; }
friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
return {LHS * RHS.MinVal, RHS.IsScalable};
}
// Additional convenience operators needed to avoid ambiguous parses.
// TODO: Make uint64_t the default operator?
TypeSize operator*(uint64_t RHS) const { return {MinVal * RHS, IsScalable}; }
TypeSize operator*(int RHS) const { return {MinVal * RHS, IsScalable}; }
TypeSize operator*(int64_t RHS) const { return {MinVal * RHS, IsScalable}; }
friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
return {LHS * RHS.MinVal, RHS.IsScalable};
}
friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
return {LHS * RHS.MinVal, RHS.IsScalable};
}
friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
return {LHS * RHS.MinVal, RHS.IsScalable};
}
};
/// Returns a TypeSize with a known minimum size that is the next integer
/// (mod 2**64) that is greater than or equal to \p Value and is a multiple
/// of \p Align. \p Align must be non-zero.
///
/// Similar to the alignTo functions in MathExtras.h
inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
assert(Align != 0u && "Align must be non-zero");
return {(Size.getKnownMinValue() + Align - 1) / Align * Align,
Size.isScalable()};
}
template <> struct DenseMapInfo<ElementCount> {
static inline ElementCount getEmptyKey() {
return ElementCount::getScalable(~0U);
}
static inline ElementCount getTombstoneKey() {
return ElementCount::getFixed(~0U - 1);
}
static unsigned getHashValue(const ElementCount& EltCnt) {
unsigned HashVal = EltCnt.getKnownMinValue() * 37U;
if (EltCnt.isScalable())
return (HashVal - 1U);
return HashVal;
}
static bool isEqual(const ElementCount& LHS, const ElementCount& RHS) {
return LHS == RHS;
}
};
} // end namespace llvm
#endif // LLVM_SUPPORT_TypeSize_H