195aff23eSKrzysztof Drewniak //===- InferIntRangeInterface.cpp -  Integer range inference interface ---===//
295aff23eSKrzysztof Drewniak //
395aff23eSKrzysztof Drewniak // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
495aff23eSKrzysztof Drewniak // See https://llvm.org/LICENSE.txt for license information.
595aff23eSKrzysztof Drewniak // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
695aff23eSKrzysztof Drewniak //
795aff23eSKrzysztof Drewniak //===----------------------------------------------------------------------===//
895aff23eSKrzysztof Drewniak 
995aff23eSKrzysztof Drewniak #include "mlir/Interfaces/InferIntRangeInterface.h"
1095aff23eSKrzysztof Drewniak #include "mlir/IR/BuiltinTypes.h"
1195aff23eSKrzysztof Drewniak #include "mlir/Interfaces/InferIntRangeInterface.cpp.inc"
1295aff23eSKrzysztof Drewniak 
1395aff23eSKrzysztof Drewniak using namespace mlir;
1495aff23eSKrzysztof Drewniak 
operator ==(const ConstantIntRanges & other) const1595aff23eSKrzysztof Drewniak bool ConstantIntRanges::operator==(const ConstantIntRanges &other) const {
1695aff23eSKrzysztof Drewniak   return umin().getBitWidth() == other.umin().getBitWidth() &&
1795aff23eSKrzysztof Drewniak          umin() == other.umin() && umax() == other.umax() &&
1895aff23eSKrzysztof Drewniak          smin() == other.smin() && smax() == other.smax();
1995aff23eSKrzysztof Drewniak }
2095aff23eSKrzysztof Drewniak 
umin() const2195aff23eSKrzysztof Drewniak const APInt &ConstantIntRanges::umin() const { return uminVal; }
2295aff23eSKrzysztof Drewniak 
umax() const2395aff23eSKrzysztof Drewniak const APInt &ConstantIntRanges::umax() const { return umaxVal; }
2495aff23eSKrzysztof Drewniak 
smin() const2595aff23eSKrzysztof Drewniak const APInt &ConstantIntRanges::smin() const { return sminVal; }
2695aff23eSKrzysztof Drewniak 
smax() const2795aff23eSKrzysztof Drewniak const APInt &ConstantIntRanges::smax() const { return smaxVal; }
2895aff23eSKrzysztof Drewniak 
getStorageBitwidth(Type type)2995aff23eSKrzysztof Drewniak unsigned ConstantIntRanges::getStorageBitwidth(Type type) {
3095aff23eSKrzysztof Drewniak   if (type.isIndex())
3195aff23eSKrzysztof Drewniak     return IndexType::kInternalStorageBitWidth;
3295aff23eSKrzysztof Drewniak   if (auto integerType = type.dyn_cast<IntegerType>())
3395aff23eSKrzysztof Drewniak     return integerType.getWidth();
3495aff23eSKrzysztof Drewniak   // Non-integer types have their bounds stored in width 0 `APInt`s.
3595aff23eSKrzysztof Drewniak   return 0;
3695aff23eSKrzysztof Drewniak }
3795aff23eSKrzysztof Drewniak 
maxRange(unsigned bitwidth)38*75bfc6f2SKrzysztof Drewniak ConstantIntRanges ConstantIntRanges::maxRange(unsigned bitwidth) {
39*75bfc6f2SKrzysztof Drewniak   return fromUnsigned(APInt::getZero(bitwidth), APInt::getMaxValue(bitwidth));
40*75bfc6f2SKrzysztof Drewniak }
41*75bfc6f2SKrzysztof Drewniak 
constant(const APInt & value)42*75bfc6f2SKrzysztof Drewniak ConstantIntRanges ConstantIntRanges::constant(const APInt &value) {
43*75bfc6f2SKrzysztof Drewniak   return {value, value, value, value};
44*75bfc6f2SKrzysztof Drewniak }
45*75bfc6f2SKrzysztof Drewniak 
range(const APInt & min,const APInt & max,bool isSigned)46*75bfc6f2SKrzysztof Drewniak ConstantIntRanges ConstantIntRanges::range(const APInt &min, const APInt &max,
47*75bfc6f2SKrzysztof Drewniak                                            bool isSigned) {
48*75bfc6f2SKrzysztof Drewniak   if (isSigned)
49*75bfc6f2SKrzysztof Drewniak     return fromSigned(min, max);
50*75bfc6f2SKrzysztof Drewniak   return fromUnsigned(min, max);
5195aff23eSKrzysztof Drewniak }
5295aff23eSKrzysztof Drewniak 
fromSigned(const APInt & smin,const APInt & smax)5395aff23eSKrzysztof Drewniak ConstantIntRanges ConstantIntRanges::fromSigned(const APInt &smin,
5495aff23eSKrzysztof Drewniak                                                 const APInt &smax) {
5595aff23eSKrzysztof Drewniak   unsigned int width = smin.getBitWidth();
5695aff23eSKrzysztof Drewniak   APInt umin, umax;
5795aff23eSKrzysztof Drewniak   if (smin.isNonNegative() == smax.isNonNegative()) {
5895aff23eSKrzysztof Drewniak     umin = smin.ult(smax) ? smin : smax;
5995aff23eSKrzysztof Drewniak     umax = smin.ugt(smax) ? smin : smax;
6095aff23eSKrzysztof Drewniak   } else {
6195aff23eSKrzysztof Drewniak     umin = APInt::getMinValue(width);
6295aff23eSKrzysztof Drewniak     umax = APInt::getMaxValue(width);
6395aff23eSKrzysztof Drewniak   }
6495aff23eSKrzysztof Drewniak   return {umin, umax, smin, smax};
6595aff23eSKrzysztof Drewniak }
6695aff23eSKrzysztof Drewniak 
fromUnsigned(const APInt & umin,const APInt & umax)6795aff23eSKrzysztof Drewniak ConstantIntRanges ConstantIntRanges::fromUnsigned(const APInt &umin,
6895aff23eSKrzysztof Drewniak                                                   const APInt &umax) {
6995aff23eSKrzysztof Drewniak   unsigned int width = umin.getBitWidth();
7095aff23eSKrzysztof Drewniak   APInt smin, smax;
7195aff23eSKrzysztof Drewniak   if (umin.isNonNegative() == umax.isNonNegative()) {
7295aff23eSKrzysztof Drewniak     smin = umin.slt(umax) ? umin : umax;
7395aff23eSKrzysztof Drewniak     smax = umin.sgt(umax) ? umin : umax;
7495aff23eSKrzysztof Drewniak   } else {
7595aff23eSKrzysztof Drewniak     smin = APInt::getSignedMinValue(width);
7695aff23eSKrzysztof Drewniak     smax = APInt::getSignedMaxValue(width);
7795aff23eSKrzysztof Drewniak   }
7895aff23eSKrzysztof Drewniak   return {umin, umax, smin, smax};
7995aff23eSKrzysztof Drewniak }
8095aff23eSKrzysztof Drewniak 
8195aff23eSKrzysztof Drewniak ConstantIntRanges
rangeUnion(const ConstantIntRanges & other) const8295aff23eSKrzysztof Drewniak ConstantIntRanges::rangeUnion(const ConstantIntRanges &other) const {
8395aff23eSKrzysztof Drewniak   // "Not an integer" poisons everything and also cannot be fed to comparison
8495aff23eSKrzysztof Drewniak   // operators.
8595aff23eSKrzysztof Drewniak   if (umin().getBitWidth() == 0)
8695aff23eSKrzysztof Drewniak     return *this;
8795aff23eSKrzysztof Drewniak   if (other.umin().getBitWidth() == 0)
8895aff23eSKrzysztof Drewniak     return other;
8995aff23eSKrzysztof Drewniak 
9095aff23eSKrzysztof Drewniak   const APInt &uminUnion = umin().ult(other.umin()) ? umin() : other.umin();
9195aff23eSKrzysztof Drewniak   const APInt &umaxUnion = umax().ugt(other.umax()) ? umax() : other.umax();
9295aff23eSKrzysztof Drewniak   const APInt &sminUnion = smin().slt(other.smin()) ? smin() : other.smin();
9395aff23eSKrzysztof Drewniak   const APInt &smaxUnion = smax().sgt(other.smax()) ? smax() : other.smax();
9495aff23eSKrzysztof Drewniak 
9595aff23eSKrzysztof Drewniak   return {uminUnion, umaxUnion, sminUnion, smaxUnion};
9695aff23eSKrzysztof Drewniak }
9795aff23eSKrzysztof Drewniak 
98*75bfc6f2SKrzysztof Drewniak ConstantIntRanges
intersection(const ConstantIntRanges & other) const99*75bfc6f2SKrzysztof Drewniak ConstantIntRanges::intersection(const ConstantIntRanges &other) const {
100*75bfc6f2SKrzysztof Drewniak   // "Not an integer" poisons everything and also cannot be fed to comparison
101*75bfc6f2SKrzysztof Drewniak   // operators.
102*75bfc6f2SKrzysztof Drewniak   if (umin().getBitWidth() == 0)
103*75bfc6f2SKrzysztof Drewniak     return *this;
104*75bfc6f2SKrzysztof Drewniak   if (other.umin().getBitWidth() == 0)
105*75bfc6f2SKrzysztof Drewniak     return other;
106*75bfc6f2SKrzysztof Drewniak 
107*75bfc6f2SKrzysztof Drewniak   const APInt &uminIntersect = umin().ugt(other.umin()) ? umin() : other.umin();
108*75bfc6f2SKrzysztof Drewniak   const APInt &umaxIntersect = umax().ult(other.umax()) ? umax() : other.umax();
109*75bfc6f2SKrzysztof Drewniak   const APInt &sminIntersect = smin().sgt(other.smin()) ? smin() : other.smin();
110*75bfc6f2SKrzysztof Drewniak   const APInt &smaxIntersect = smax().slt(other.smax()) ? smax() : other.smax();
111*75bfc6f2SKrzysztof Drewniak 
112*75bfc6f2SKrzysztof Drewniak   return {uminIntersect, umaxIntersect, sminIntersect, smaxIntersect};
113*75bfc6f2SKrzysztof Drewniak }
114*75bfc6f2SKrzysztof Drewniak 
getConstantValue() const11595aff23eSKrzysztof Drewniak Optional<APInt> ConstantIntRanges::getConstantValue() const {
11695aff23eSKrzysztof Drewniak   // Note: we need to exclude the trivially-equal width 0 values here.
11795aff23eSKrzysztof Drewniak   if (umin() == umax() && umin().getBitWidth() != 0)
11895aff23eSKrzysztof Drewniak     return umin();
11995aff23eSKrzysztof Drewniak   if (smin() == smax() && smin().getBitWidth() != 0)
12095aff23eSKrzysztof Drewniak     return smin();
12195aff23eSKrzysztof Drewniak   return None;
12295aff23eSKrzysztof Drewniak }
12395aff23eSKrzysztof Drewniak 
operator <<(raw_ostream & os,const ConstantIntRanges & range)12495aff23eSKrzysztof Drewniak raw_ostream &mlir::operator<<(raw_ostream &os, const ConstantIntRanges &range) {
12595aff23eSKrzysztof Drewniak   return os << "unsigned : [" << range.umin() << ", " << range.umax()
12695aff23eSKrzysztof Drewniak             << "] signed : [" << range.smin() << ", " << range.smax() << "]";
12795aff23eSKrzysztof Drewniak }
128