| 1 | //===- ConstantFPRange.cpp - ConstantFPRange implementation ---------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "llvm/IR/ConstantFPRange.h" |
| 10 | #include "llvm/ADT/APFloat.h" |
| 11 | #include "llvm/ADT/FloatingPointMode.h" |
| 12 | #include "llvm/Support/Debug.h" |
| 13 | #include "llvm/Support/raw_ostream.h" |
| 14 | #include <cassert> |
| 15 | |
| 16 | using namespace llvm; |
| 17 | |
| 18 | void ConstantFPRange::makeEmpty() { |
| 19 | auto &Sem = Lower.getSemantics(); |
| 20 | Lower = APFloat::getInf(Sem, /*Negative=*/false); |
| 21 | Upper = APFloat::getInf(Sem, /*Negative=*/true); |
| 22 | MayBeQNaN = false; |
| 23 | MayBeSNaN = false; |
| 24 | } |
| 25 | |
| 26 | void ConstantFPRange::makeFull() { |
| 27 | auto &Sem = Lower.getSemantics(); |
| 28 | Lower = APFloat::getInf(Sem, /*Negative=*/true); |
| 29 | Upper = APFloat::getInf(Sem, /*Negative=*/false); |
| 30 | MayBeQNaN = true; |
| 31 | MayBeSNaN = true; |
| 32 | } |
| 33 | |
| 34 | bool ConstantFPRange::isNaNOnly() const { |
| 35 | return Lower.isPosInfinity() && Upper.isNegInfinity(); |
| 36 | } |
| 37 | |
| 38 | ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet) |
| 39 | : Lower(Sem, APFloat::uninitialized), Upper(Sem, APFloat::uninitialized) { |
| 40 | Lower = APFloat::getInf(Sem, /*Negative=*/IsFullSet); |
| 41 | Upper = APFloat::getInf(Sem, /*Negative=*/!IsFullSet); |
| 42 | MayBeQNaN = IsFullSet; |
| 43 | MayBeSNaN = IsFullSet; |
| 44 | } |
| 45 | |
| 46 | ConstantFPRange::ConstantFPRange(const APFloat &Value) |
| 47 | : Lower(Value.getSemantics(), APFloat::uninitialized), |
| 48 | Upper(Value.getSemantics(), APFloat::uninitialized) { |
| 49 | if (Value.isNaN()) { |
| 50 | makeEmpty(); |
| 51 | bool IsSNaN = Value.isSignaling(); |
| 52 | MayBeQNaN = !IsSNaN; |
| 53 | MayBeSNaN = IsSNaN; |
| 54 | } else { |
| 55 | Lower = Upper = Value; |
| 56 | MayBeQNaN = MayBeSNaN = false; |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // We treat that -0 is less than 0 here. |
| 61 | static APFloat::cmpResult strictCompare(const APFloat &LHS, |
| 62 | const APFloat &RHS) { |
| 63 | assert(!LHS.isNaN() && !RHS.isNaN() && "Unordered compare" ); |
| 64 | if (LHS.isZero() && RHS.isZero()) { |
| 65 | if (LHS.isNegative() == RHS.isNegative()) |
| 66 | return APFloat::cmpEqual; |
| 67 | return LHS.isNegative() ? APFloat::cmpLessThan : APFloat::cmpGreaterThan; |
| 68 | } |
| 69 | return LHS.compare(RHS); |
| 70 | } |
| 71 | |
| 72 | static bool isNonCanonicalEmptySet(const APFloat &Lower, const APFloat &Upper) { |
| 73 | return strictCompare(LHS: Lower, RHS: Upper) == APFloat::cmpGreaterThan && |
| 74 | !(Lower.isInfinity() && Upper.isInfinity()); |
| 75 | } |
| 76 | |
| 77 | static void canonicalizeRange(APFloat &Lower, APFloat &Upper) { |
| 78 | if (isNonCanonicalEmptySet(Lower, Upper)) { |
| 79 | Lower = APFloat::getInf(Sem: Lower.getSemantics(), /*Negative=*/false); |
| 80 | Upper = APFloat::getInf(Sem: Upper.getSemantics(), /*Negative=*/true); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal, |
| 85 | bool MayBeQNaNVal, bool MayBeSNaNVal) |
| 86 | : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)), |
| 87 | MayBeQNaN(MayBeQNaNVal), MayBeSNaN(MayBeSNaNVal) { |
| 88 | assert(&Lower.getSemantics() == &Upper.getSemantics() && |
| 89 | "Should only use the same semantics" ); |
| 90 | assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form" ); |
| 91 | } |
| 92 | |
| 93 | ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) { |
| 94 | return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true), |
| 95 | APFloat::getLargest(Sem, /*Negative=*/false), |
| 96 | /*MayBeQNaN=*/false, /*MayBeSNaN=*/false); |
| 97 | } |
| 98 | |
| 99 | ConstantFPRange ConstantFPRange::getNaNOnly(const fltSemantics &Sem, |
| 100 | bool MayBeQNaN, bool MayBeSNaN) { |
| 101 | return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false), |
| 102 | APFloat::getInf(Sem, /*Negative=*/true), MayBeQNaN, |
| 103 | MayBeSNaN); |
| 104 | } |
| 105 | |
| 106 | ConstantFPRange ConstantFPRange::getNonNaN(const fltSemantics &Sem) { |
| 107 | return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true), |
| 108 | APFloat::getInf(Sem, /*Negative=*/false), |
| 109 | /*MayBeQNaN=*/false, /*MayBeSNaN=*/false); |
| 110 | } |
| 111 | |
| 112 | /// Return true for ULT/UGT/OLT/OGT |
| 113 | static bool fcmpPredExcludesEqual(FCmpInst::Predicate Pred) { |
| 114 | return !(Pred & FCmpInst::FCMP_OEQ); |
| 115 | } |
| 116 | |
| 117 | /// Return [-inf, V) or [-inf, V] |
| 118 | static ConstantFPRange makeLessThan(APFloat V, FCmpInst::Predicate Pred) { |
| 119 | const fltSemantics &Sem = V.getSemantics(); |
| 120 | if (fcmpPredExcludesEqual(Pred)) { |
| 121 | if (V.isNegInfinity()) |
| 122 | return ConstantFPRange::getEmpty(Sem); |
| 123 | V.next(/*nextDown=*/true); |
| 124 | } |
| 125 | return ConstantFPRange::getNonNaN(LowerVal: APFloat::getInf(Sem, /*Negative=*/true), |
| 126 | UpperVal: std::move(V)); |
| 127 | } |
| 128 | |
| 129 | /// Return (V, +inf] or [V, +inf] |
| 130 | static ConstantFPRange makeGreaterThan(APFloat V, FCmpInst::Predicate Pred) { |
| 131 | const fltSemantics &Sem = V.getSemantics(); |
| 132 | if (fcmpPredExcludesEqual(Pred)) { |
| 133 | if (V.isPosInfinity()) |
| 134 | return ConstantFPRange::getEmpty(Sem); |
| 135 | V.next(/*nextDown=*/false); |
| 136 | } |
| 137 | return ConstantFPRange::getNonNaN(LowerVal: std::move(V), |
| 138 | UpperVal: APFloat::getInf(Sem, /*Negative=*/false)); |
| 139 | } |
| 140 | |
| 141 | /// Make sure that +0/-0 are both included in the range. |
| 142 | static ConstantFPRange extendZeroIfEqual(const ConstantFPRange &CR, |
| 143 | FCmpInst::Predicate Pred) { |
| 144 | if (fcmpPredExcludesEqual(Pred)) |
| 145 | return CR; |
| 146 | |
| 147 | APFloat Lower = CR.getLower(); |
| 148 | APFloat Upper = CR.getUpper(); |
| 149 | if (Lower.isPosZero()) |
| 150 | Lower = APFloat::getZero(Sem: Lower.getSemantics(), /*Negative=*/true); |
| 151 | if (Upper.isNegZero()) |
| 152 | Upper = APFloat::getZero(Sem: Upper.getSemantics(), /*Negative=*/false); |
| 153 | return ConstantFPRange(std::move(Lower), std::move(Upper), CR.containsQNaN(), |
| 154 | CR.containsSNaN()); |
| 155 | } |
| 156 | |
| 157 | static ConstantFPRange setNaNField(const ConstantFPRange &CR, |
| 158 | FCmpInst::Predicate Pred) { |
| 159 | bool ContainsNaN = FCmpInst::isUnordered(predicate: Pred); |
| 160 | return ConstantFPRange(CR.getLower(), CR.getUpper(), |
| 161 | /*MayBeQNaN=*/ContainsNaN, /*MayBeSNaN=*/ContainsNaN); |
| 162 | } |
| 163 | |
| 164 | ConstantFPRange |
| 165 | ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred, |
| 166 | const ConstantFPRange &Other) { |
| 167 | if (Other.isEmptySet()) |
| 168 | return Other; |
| 169 | if (Other.containsNaN() && FCmpInst::isUnordered(predicate: Pred)) |
| 170 | return getFull(Sem: Other.getSemantics()); |
| 171 | if (Other.isNaNOnly() && FCmpInst::isOrdered(predicate: Pred)) |
| 172 | return getEmpty(Sem: Other.getSemantics()); |
| 173 | |
| 174 | switch (Pred) { |
| 175 | case FCmpInst::FCMP_TRUE: |
| 176 | return getFull(Sem: Other.getSemantics()); |
| 177 | case FCmpInst::FCMP_FALSE: |
| 178 | return getEmpty(Sem: Other.getSemantics()); |
| 179 | case FCmpInst::FCMP_ORD: |
| 180 | return getNonNaN(Sem: Other.getSemantics()); |
| 181 | case FCmpInst::FCMP_UNO: |
| 182 | return getNaNOnly(Sem: Other.getSemantics(), /*MayBeQNaN=*/true, |
| 183 | /*MayBeSNaN=*/true); |
| 184 | case FCmpInst::FCMP_OEQ: |
| 185 | case FCmpInst::FCMP_UEQ: |
| 186 | return setNaNField(CR: extendZeroIfEqual(CR: Other, Pred), Pred); |
| 187 | case FCmpInst::FCMP_ONE: |
| 188 | case FCmpInst::FCMP_UNE: |
| 189 | if (const APFloat *SingleElement = |
| 190 | Other.getSingleElement(/*ExcludesNaN=*/true)) { |
| 191 | const fltSemantics &Sem = SingleElement->getSemantics(); |
| 192 | if (SingleElement->isPosInfinity()) |
| 193 | return setNaNField( |
| 194 | CR: getNonNaN(LowerVal: APFloat::getInf(Sem, /*Negative=*/true), |
| 195 | UpperVal: APFloat::getLargest(Sem, /*Negative=*/false)), |
| 196 | Pred); |
| 197 | if (SingleElement->isNegInfinity()) |
| 198 | return setNaNField( |
| 199 | CR: getNonNaN(LowerVal: APFloat::getLargest(Sem, /*Negative=*/true), |
| 200 | UpperVal: APFloat::getInf(Sem, /*Negative=*/false)), |
| 201 | Pred); |
| 202 | } |
| 203 | return Pred == FCmpInst::FCMP_ONE ? getNonNaN(Sem: Other.getSemantics()) |
| 204 | : getFull(Sem: Other.getSemantics()); |
| 205 | case FCmpInst::FCMP_OLT: |
| 206 | case FCmpInst::FCMP_OLE: |
| 207 | case FCmpInst::FCMP_ULT: |
| 208 | case FCmpInst::FCMP_ULE: |
| 209 | return setNaNField( |
| 210 | CR: extendZeroIfEqual(CR: makeLessThan(V: Other.getUpper(), Pred), Pred), Pred); |
| 211 | case FCmpInst::FCMP_OGT: |
| 212 | case FCmpInst::FCMP_OGE: |
| 213 | case FCmpInst::FCMP_UGT: |
| 214 | case FCmpInst::FCMP_UGE: |
| 215 | return setNaNField( |
| 216 | CR: extendZeroIfEqual(CR: makeGreaterThan(V: Other.getLower(), Pred), Pred), Pred); |
| 217 | default: |
| 218 | llvm_unreachable("Unexpected predicate" ); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | ConstantFPRange |
| 223 | ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred, |
| 224 | const ConstantFPRange &Other) { |
| 225 | if (Other.isEmptySet()) |
| 226 | return getFull(Sem: Other.getSemantics()); |
| 227 | if (Other.containsNaN() && FCmpInst::isOrdered(predicate: Pred)) |
| 228 | return getEmpty(Sem: Other.getSemantics()); |
| 229 | if (Other.isNaNOnly() && FCmpInst::isUnordered(predicate: Pred)) |
| 230 | return getFull(Sem: Other.getSemantics()); |
| 231 | |
| 232 | switch (Pred) { |
| 233 | case FCmpInst::FCMP_TRUE: |
| 234 | return getFull(Sem: Other.getSemantics()); |
| 235 | case FCmpInst::FCMP_FALSE: |
| 236 | return getEmpty(Sem: Other.getSemantics()); |
| 237 | case FCmpInst::FCMP_ORD: |
| 238 | return getNonNaN(Sem: Other.getSemantics()); |
| 239 | case FCmpInst::FCMP_UNO: |
| 240 | return getNaNOnly(Sem: Other.getSemantics(), /*MayBeQNaN=*/true, |
| 241 | /*MayBeSNaN=*/true); |
| 242 | case FCmpInst::FCMP_OEQ: |
| 243 | case FCmpInst::FCMP_UEQ: |
| 244 | return setNaNField(CR: Other.isSingleElement(/*ExcludesNaN=*/true) || |
| 245 | ((Other.classify() & ~fcNan) == fcZero) |
| 246 | ? extendZeroIfEqual(CR: Other, Pred) |
| 247 | : getEmpty(Sem: Other.getSemantics()), |
| 248 | Pred); |
| 249 | case FCmpInst::FCMP_ONE: |
| 250 | case FCmpInst::FCMP_UNE: |
| 251 | return getEmpty(Sem: Other.getSemantics()); |
| 252 | case FCmpInst::FCMP_OLT: |
| 253 | case FCmpInst::FCMP_OLE: |
| 254 | case FCmpInst::FCMP_ULT: |
| 255 | case FCmpInst::FCMP_ULE: |
| 256 | return setNaNField( |
| 257 | CR: extendZeroIfEqual(CR: makeLessThan(V: Other.getLower(), Pred), Pred), Pred); |
| 258 | case FCmpInst::FCMP_OGT: |
| 259 | case FCmpInst::FCMP_OGE: |
| 260 | case FCmpInst::FCMP_UGT: |
| 261 | case FCmpInst::FCMP_UGE: |
| 262 | return setNaNField( |
| 263 | CR: extendZeroIfEqual(CR: makeGreaterThan(V: Other.getUpper(), Pred), Pred), Pred); |
| 264 | default: |
| 265 | llvm_unreachable("Unexpected predicate" ); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | std::optional<ConstantFPRange> |
| 270 | ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred, |
| 271 | const APFloat &Other) { |
| 272 | if ((Pred == FCmpInst::FCMP_UNE || Pred == FCmpInst::FCMP_ONE) && |
| 273 | !Other.isNaN()) |
| 274 | return std::nullopt; |
| 275 | return makeSatisfyingFCmpRegion(Pred, Other: ConstantFPRange(Other)); |
| 276 | } |
| 277 | |
| 278 | bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred, |
| 279 | const ConstantFPRange &Other) const { |
| 280 | return makeSatisfyingFCmpRegion(Pred, Other).contains(CR: *this); |
| 281 | } |
| 282 | |
| 283 | bool ConstantFPRange::isFullSet() const { |
| 284 | return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN && |
| 285 | MayBeSNaN; |
| 286 | } |
| 287 | |
| 288 | bool ConstantFPRange::isEmptySet() const { |
| 289 | return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN && |
| 290 | !MayBeSNaN; |
| 291 | } |
| 292 | |
| 293 | bool ConstantFPRange::contains(const APFloat &Val) const { |
| 294 | assert(&getSemantics() == &Val.getSemantics() && |
| 295 | "Should only use the same semantics" ); |
| 296 | |
| 297 | if (Val.isNaN()) |
| 298 | return Val.isSignaling() ? MayBeSNaN : MayBeQNaN; |
| 299 | return strictCompare(LHS: Lower, RHS: Val) != APFloat::cmpGreaterThan && |
| 300 | strictCompare(LHS: Val, RHS: Upper) != APFloat::cmpGreaterThan; |
| 301 | } |
| 302 | |
| 303 | bool ConstantFPRange::contains(const ConstantFPRange &CR) const { |
| 304 | assert(&getSemantics() == &CR.getSemantics() && |
| 305 | "Should only use the same semantics" ); |
| 306 | |
| 307 | if (CR.MayBeQNaN && !MayBeQNaN) |
| 308 | return false; |
| 309 | |
| 310 | if (CR.MayBeSNaN && !MayBeSNaN) |
| 311 | return false; |
| 312 | |
| 313 | return strictCompare(LHS: Lower, RHS: CR.Lower) != APFloat::cmpGreaterThan && |
| 314 | strictCompare(LHS: CR.Upper, RHS: Upper) != APFloat::cmpGreaterThan; |
| 315 | } |
| 316 | |
| 317 | const APFloat *ConstantFPRange::getSingleElement(bool ExcludesNaN) const { |
| 318 | if (!ExcludesNaN && (MayBeSNaN || MayBeQNaN)) |
| 319 | return nullptr; |
| 320 | return Lower.bitwiseIsEqual(RHS: Upper) ? &Lower : nullptr; |
| 321 | } |
| 322 | |
| 323 | std::optional<bool> ConstantFPRange::getSignBit() const { |
| 324 | if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative()) |
| 325 | return Lower.isNegative(); |
| 326 | return std::nullopt; |
| 327 | } |
| 328 | |
| 329 | bool ConstantFPRange::operator==(const ConstantFPRange &CR) const { |
| 330 | assert(&getSemantics() == &CR.getSemantics() && |
| 331 | "Should only use the same semantics" ); |
| 332 | if (MayBeSNaN != CR.MayBeSNaN || MayBeQNaN != CR.MayBeQNaN) |
| 333 | return false; |
| 334 | return Lower.bitwiseIsEqual(RHS: CR.Lower) && Upper.bitwiseIsEqual(RHS: CR.Upper); |
| 335 | } |
| 336 | |
| 337 | FPClassTest ConstantFPRange::classify() const { |
| 338 | uint32_t Mask = fcNone; |
| 339 | if (MayBeSNaN) |
| 340 | Mask |= fcSNan; |
| 341 | if (MayBeQNaN) |
| 342 | Mask |= fcQNan; |
| 343 | if (!isNaNOnly()) { |
| 344 | FPClassTest LowerMask = Lower.classify(); |
| 345 | FPClassTest UpperMask = Upper.classify(); |
| 346 | assert(LowerMask <= UpperMask && "Range is nan-only." ); |
| 347 | // Set all bits from log2(LowerMask) to log2(UpperMask). |
| 348 | Mask |= (UpperMask << 1) - LowerMask; |
| 349 | } |
| 350 | return static_cast<FPClassTest>(Mask); |
| 351 | } |
| 352 | |
| 353 | void ConstantFPRange::print(raw_ostream &OS) const { |
| 354 | if (isFullSet()) |
| 355 | OS << "full-set" ; |
| 356 | else if (isEmptySet()) |
| 357 | OS << "empty-set" ; |
| 358 | else { |
| 359 | bool NaNOnly = isNaNOnly(); |
| 360 | if (!NaNOnly) |
| 361 | OS << '[' << Lower << ", " << Upper << ']'; |
| 362 | |
| 363 | if (MayBeSNaN || MayBeQNaN) { |
| 364 | if (!NaNOnly) |
| 365 | OS << " with " ; |
| 366 | if (MayBeSNaN && MayBeQNaN) |
| 367 | OS << "NaN" ; |
| 368 | else if (MayBeSNaN) |
| 369 | OS << "SNaN" ; |
| 370 | else if (MayBeQNaN) |
| 371 | OS << "QNaN" ; |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| 377 | LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); } |
| 378 | #endif |
| 379 | |
| 380 | ConstantFPRange |
| 381 | ConstantFPRange::intersectWith(const ConstantFPRange &CR) const { |
| 382 | assert(&getSemantics() == &CR.getSemantics() && |
| 383 | "Should only use the same semantics" ); |
| 384 | APFloat NewLower = maxnum(A: Lower, B: CR.Lower); |
| 385 | APFloat NewUpper = minnum(A: Upper, B: CR.Upper); |
| 386 | canonicalizeRange(Lower&: NewLower, Upper&: NewUpper); |
| 387 | return ConstantFPRange(std::move(NewLower), std::move(NewUpper), |
| 388 | MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN); |
| 389 | } |
| 390 | |
| 391 | ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const { |
| 392 | assert(&getSemantics() == &CR.getSemantics() && |
| 393 | "Should only use the same semantics" ); |
| 394 | return ConstantFPRange(minnum(A: Lower, B: CR.Lower), maxnum(A: Upper, B: CR.Upper), |
| 395 | MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN); |
| 396 | } |
| 397 | |
| 398 | ConstantFPRange ConstantFPRange::abs() const { |
| 399 | if (isNaNOnly()) |
| 400 | return *this; |
| 401 | // Check if the range is all non-negative or all non-positive. |
| 402 | if (Lower.isNegative() == Upper.isNegative()) { |
| 403 | if (Lower.isNegative()) |
| 404 | return negate(); |
| 405 | return *this; |
| 406 | } |
| 407 | // The range contains both positive and negative values. |
| 408 | APFloat NewLower = APFloat::getZero(Sem: getSemantics()); |
| 409 | APFloat NewUpper = maxnum(A: -Lower, B: Upper); |
| 410 | return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN, |
| 411 | MayBeSNaN); |
| 412 | } |
| 413 | |
| 414 | ConstantFPRange ConstantFPRange::negate() const { |
| 415 | return ConstantFPRange(-Upper, -Lower, MayBeQNaN, MayBeSNaN); |
| 416 | } |
| 417 | |
| 418 | /// Return true if the finite part is not empty after removing infinities. |
| 419 | static bool removeInf(APFloat &Lower, APFloat &Upper, bool &HasPosInf, |
| 420 | bool &HasNegInf) { |
| 421 | assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && |
| 422 | "Non-NaN part is empty." ); |
| 423 | auto &Sem = Lower.getSemantics(); |
| 424 | if (Lower.isNegInfinity()) { |
| 425 | Lower = APFloat::getLargest(Sem, /*Negative=*/true); |
| 426 | HasNegInf = true; |
| 427 | } |
| 428 | if (Upper.isPosInfinity()) { |
| 429 | Upper = APFloat::getLargest(Sem, /*Negative=*/false); |
| 430 | HasPosInf = true; |
| 431 | } |
| 432 | return strictCompare(LHS: Lower, RHS: Upper) != APFloat::cmpGreaterThan; |
| 433 | } |
| 434 | |
| 435 | ConstantFPRange ConstantFPRange::getWithoutInf() const { |
| 436 | if (isNaNOnly()) |
| 437 | return *this; |
| 438 | APFloat NewLower = Lower; |
| 439 | APFloat NewUpper = Upper; |
| 440 | bool UnusedFlag; |
| 441 | removeInf(Lower&: NewLower, Upper&: NewUpper, /*HasPosInf=*/UnusedFlag, |
| 442 | /*HasNegInf=*/UnusedFlag); |
| 443 | canonicalizeRange(Lower&: NewLower, Upper&: NewUpper); |
| 444 | return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN, |
| 445 | MayBeSNaN); |
| 446 | } |
| 447 | |
| 448 | ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem, |
| 449 | APFloat::roundingMode RM) const { |
| 450 | bool LosesInfo; |
| 451 | APFloat NewLower = Lower; |
| 452 | APFloat NewUpper = Upper; |
| 453 | // For conservative, return full range if conversion is invalid. |
| 454 | if (NewLower.convert(ToSemantics: DstSem, RM, losesInfo: &LosesInfo) == APFloat::opInvalidOp || |
| 455 | NewLower.isNaN()) |
| 456 | return getFull(Sem: DstSem); |
| 457 | if (NewUpper.convert(ToSemantics: DstSem, RM, losesInfo: &LosesInfo) == APFloat::opInvalidOp || |
| 458 | NewUpper.isNaN()) |
| 459 | return getFull(Sem: DstSem); |
| 460 | return ConstantFPRange(std::move(NewLower), std::move(NewUpper), |
| 461 | /*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN, |
| 462 | /*MayBeSNaNVal=*/false); |
| 463 | } |
| 464 | |
| 465 | ConstantFPRange ConstantFPRange::add(const ConstantFPRange &Other) const { |
| 466 | bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || |
| 467 | ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); |
| 468 | if (isNaNOnly() || Other.isNaNOnly()) |
| 469 | return getNaNOnly(Sem: getSemantics(), /*MayBeQNaN=*/ResMayBeQNaN, |
| 470 | /*MayBeSNaN=*/false); |
| 471 | bool LHSHasNegInf = false, LHSHasPosInf = false; |
| 472 | APFloat LHSLower = Lower, LHSUpper = Upper; |
| 473 | bool LHSFiniteIsNonEmpty = |
| 474 | removeInf(Lower&: LHSLower, Upper&: LHSUpper, HasPosInf&: LHSHasPosInf, HasNegInf&: LHSHasNegInf); |
| 475 | bool RHSHasNegInf = false, RHSHasPosInf = false; |
| 476 | APFloat RHSLower = Other.Lower, RHSUpper = Other.Upper; |
| 477 | bool RHSFiniteIsNonEmpty = |
| 478 | removeInf(Lower&: RHSLower, Upper&: RHSUpper, HasPosInf&: RHSHasPosInf, HasNegInf&: RHSHasNegInf); |
| 479 | // -inf + +inf = QNaN |
| 480 | ResMayBeQNaN |= |
| 481 | (LHSHasNegInf && RHSHasPosInf) || (LHSHasPosInf && RHSHasNegInf); |
| 482 | // +inf + finite/+inf = +inf, -inf + finite/-inf = -inf |
| 483 | bool HasNegInf = (LHSHasNegInf && (RHSFiniteIsNonEmpty || RHSHasNegInf)) || |
| 484 | (RHSHasNegInf && (LHSFiniteIsNonEmpty || LHSHasNegInf)); |
| 485 | bool HasPosInf = (LHSHasPosInf && (RHSFiniteIsNonEmpty || RHSHasPosInf)) || |
| 486 | (RHSHasPosInf && (LHSFiniteIsNonEmpty || LHSHasPosInf)); |
| 487 | if (LHSFiniteIsNonEmpty && RHSFiniteIsNonEmpty) { |
| 488 | APFloat NewLower = |
| 489 | HasNegInf ? APFloat::getInf(Sem: LHSLower.getSemantics(), /*Negative=*/true) |
| 490 | : LHSLower + RHSLower; |
| 491 | APFloat NewUpper = |
| 492 | HasPosInf ? APFloat::getInf(Sem: LHSUpper.getSemantics(), /*Negative=*/false) |
| 493 | : LHSUpper + RHSUpper; |
| 494 | return ConstantFPRange(NewLower, NewUpper, ResMayBeQNaN, |
| 495 | /*MayBeSNaN=*/false); |
| 496 | } |
| 497 | // If both HasNegInf and HasPosInf are false, the non-NaN part is empty. |
| 498 | // We just return the canonical form [+inf, -inf] for the empty non-NaN set. |
| 499 | return ConstantFPRange( |
| 500 | APFloat::getInf(Sem: Lower.getSemantics(), /*Negative=*/HasNegInf), |
| 501 | APFloat::getInf(Sem: Upper.getSemantics(), /*Negative=*/!HasPosInf), |
| 502 | ResMayBeQNaN, |
| 503 | /*MayBeSNaN=*/false); |
| 504 | } |
| 505 | |
| 506 | ConstantFPRange ConstantFPRange::sub(const ConstantFPRange &Other) const { |
| 507 | // fsub X, Y = fadd X, (fneg Y) |
| 508 | return add(Other: Other.negate()); |
| 509 | } |
| 510 | |
| 511 | void ConstantFPRange::flushDenormals(DenormalMode::DenormalModeKind Mode) { |
| 512 | if (Mode == DenormalMode::IEEE) |
| 513 | return; |
| 514 | FPClassTest Class = classify(); |
| 515 | if (!(Class & fcSubnormal)) |
| 516 | return; |
| 517 | |
| 518 | auto &Sem = getSemantics(); |
| 519 | // PreserveSign: PosSubnormal -> PosZero, NegSubnormal -> NegZero |
| 520 | // PositiveZero: PosSubnormal -> PosZero, NegSubnormal -> PosZero |
| 521 | // Dynamic: PosSubnormal -> PosZero, NegSubnormal -> NegZero/PosZero |
| 522 | bool ZeroLowerNegative = |
| 523 | Mode != DenormalMode::PositiveZero && (Class & fcNegSubnormal); |
| 524 | bool ZeroUpperNegative = |
| 525 | Mode == DenormalMode::PreserveSign && !(Class & fcPosSubnormal); |
| 526 | assert((ZeroLowerNegative || !ZeroUpperNegative) && |
| 527 | "ZeroLower is greater than ZeroUpper." ); |
| 528 | Lower = minnum(A: Lower, B: APFloat::getZero(Sem, Negative: ZeroLowerNegative)); |
| 529 | Upper = maxnum(A: Upper, B: APFloat::getZero(Sem, Negative: ZeroUpperNegative)); |
| 530 | } |
| 531 | |
| 532 | /// Represent a contiguous range of values sharing the same sign. |
| 533 | struct SameSignRange { |
| 534 | bool HasZero; |
| 535 | bool HasNonZero; |
| 536 | bool HasInf; |
| 537 | // The lower and upper bounds of the range (inclusive). |
| 538 | // The sign is dropped and infinities are excluded. |
| 539 | std::optional<std::pair<APFloat, APFloat>> FinitePart; |
| 540 | |
| 541 | explicit SameSignRange(const APFloat &Lower, const APFloat &Upper) |
| 542 | : HasZero(Lower.isZero()), HasNonZero(!Upper.isZero()), |
| 543 | HasInf(Upper.isInfinity()) { |
| 544 | assert(!Lower.isNegative() && !Upper.isNegative() && |
| 545 | "The sign should be dropped." ); |
| 546 | assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && |
| 547 | "Empty set." ); |
| 548 | if (!Lower.isInfinity()) |
| 549 | FinitePart = {Lower, |
| 550 | HasInf ? APFloat::getLargest(Sem: Lower.getSemantics()) : Upper}; |
| 551 | } |
| 552 | }; |
| 553 | |
| 554 | /// Split the range into positive and negative components. |
| 555 | static void splitPosNeg(const APFloat &Lower, const APFloat &Upper, |
| 556 | std::optional<SameSignRange> &NegPart, |
| 557 | std::optional<SameSignRange> &PosPart) { |
| 558 | assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && |
| 559 | "Non-NaN part is empty." ); |
| 560 | if (Lower.isNegative() == Upper.isNegative()) { |
| 561 | if (Lower.isNegative()) |
| 562 | NegPart = SameSignRange{abs(X: Upper), abs(X: Lower)}; |
| 563 | else |
| 564 | PosPart = SameSignRange{Lower, Upper}; |
| 565 | return; |
| 566 | } |
| 567 | auto &Sem = Lower.getSemantics(); |
| 568 | NegPart = SameSignRange{APFloat::getZero(Sem), abs(X: Lower)}; |
| 569 | PosPart = SameSignRange{APFloat::getZero(Sem), Upper}; |
| 570 | } |
| 571 | |
| 572 | ConstantFPRange ConstantFPRange::mul(const ConstantFPRange &Other) const { |
| 573 | auto &Sem = getSemantics(); |
| 574 | bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || |
| 575 | ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); |
| 576 | if (isNaNOnly() || Other.isNaNOnly()) |
| 577 | return getNaNOnly(Sem, /*MayBeQNaN=*/ResMayBeQNaN, |
| 578 | /*MayBeSNaN=*/false); |
| 579 | std::optional<SameSignRange> LHSNeg, LHSPos, RHSNeg, RHSPos; |
| 580 | splitPosNeg(Lower, Upper, NegPart&: LHSNeg, PosPart&: LHSPos); |
| 581 | splitPosNeg(Lower: Other.Lower, Upper: Other.Upper, NegPart&: RHSNeg, PosPart&: RHSPos); |
| 582 | APFloat ResLower = APFloat::getInf(Sem, /*Negative=*/false); |
| 583 | APFloat ResUpper = APFloat::getInf(Sem, /*Negative=*/true); |
| 584 | auto Update = [&](std::optional<SameSignRange> &LHS, |
| 585 | std::optional<SameSignRange> &RHS, bool Negative) { |
| 586 | if (!LHS || !RHS) |
| 587 | return; |
| 588 | // 0 * inf = QNaN |
| 589 | ResMayBeQNaN |= LHS->HasZero && RHS->HasInf; |
| 590 | ResMayBeQNaN |= RHS->HasZero && LHS->HasInf; |
| 591 | // NonZero * inf = inf |
| 592 | if ((LHS->HasInf && RHS->HasNonZero) || (RHS->HasInf && LHS->HasNonZero)) |
| 593 | (Negative ? ResLower : ResUpper) = APFloat::getInf(Sem, Negative); |
| 594 | // Finite * Finite |
| 595 | if (LHS->FinitePart && RHS->FinitePart) { |
| 596 | APFloat NewLower = LHS->FinitePart->first * RHS->FinitePart->first; |
| 597 | APFloat NewUpper = LHS->FinitePart->second * RHS->FinitePart->second; |
| 598 | if (Negative) { |
| 599 | ResLower = minnum(A: ResLower, B: -NewUpper); |
| 600 | ResUpper = maxnum(A: ResUpper, B: -NewLower); |
| 601 | } else { |
| 602 | ResLower = minnum(A: ResLower, B: NewLower); |
| 603 | ResUpper = maxnum(A: ResUpper, B: NewUpper); |
| 604 | } |
| 605 | } |
| 606 | }; |
| 607 | Update(LHSNeg, RHSNeg, /*Negative=*/false); |
| 608 | Update(LHSNeg, RHSPos, /*Negative=*/true); |
| 609 | Update(LHSPos, RHSNeg, /*Negative=*/true); |
| 610 | Update(LHSPos, RHSPos, /*Negative=*/false); |
| 611 | return ConstantFPRange(ResLower, ResUpper, ResMayBeQNaN, /*MayBeSNaN=*/false); |
| 612 | } |
| 613 | |
| 614 | ConstantFPRange ConstantFPRange::div(const ConstantFPRange &Other) const { |
| 615 | auto &Sem = getSemantics(); |
| 616 | bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || |
| 617 | ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); |
| 618 | if (isNaNOnly() || Other.isNaNOnly()) |
| 619 | return getNaNOnly(Sem, /*MayBeQNaN=*/ResMayBeQNaN, |
| 620 | /*MayBeSNaN=*/false); |
| 621 | std::optional<SameSignRange> LHSNeg, LHSPos, RHSNeg, RHSPos; |
| 622 | splitPosNeg(Lower, Upper, NegPart&: LHSNeg, PosPart&: LHSPos); |
| 623 | splitPosNeg(Lower: Other.Lower, Upper: Other.Upper, NegPart&: RHSNeg, PosPart&: RHSPos); |
| 624 | APFloat ResLower = APFloat::getInf(Sem, /*Negative=*/false); |
| 625 | APFloat ResUpper = APFloat::getInf(Sem, /*Negative=*/true); |
| 626 | auto Update = [&](std::optional<SameSignRange> &LHS, |
| 627 | std::optional<SameSignRange> &RHS, bool Negative) { |
| 628 | if (!LHS || !RHS) |
| 629 | return; |
| 630 | // inf / inf = QNaN 0 / 0 = QNaN |
| 631 | ResMayBeQNaN |= LHS->HasInf && RHS->HasInf; |
| 632 | ResMayBeQNaN |= LHS->HasZero && RHS->HasZero; |
| 633 | // It is not straightforward to infer HasNonZeroFinite = HasFinite && |
| 634 | // HasNonZero. By definitions we have: |
| 635 | // HasFinite = HasNonZeroFinite || HasZero |
| 636 | // HasNonZero = HasNonZeroFinite || HasInf |
| 637 | // Since the range is contiguous, if both HasFinite and HasNonZero are true, |
| 638 | // HasNonZeroFinite must be true. |
| 639 | bool LHSHasNonZeroFinite = LHS->FinitePart && LHS->HasNonZero; |
| 640 | bool RHSHasNonZeroFinite = RHS->FinitePart && RHS->HasNonZero; |
| 641 | // inf / Finite = inf FiniteNonZero / 0 = inf |
| 642 | if ((LHS->HasInf && RHS->FinitePart) || |
| 643 | (LHSHasNonZeroFinite && RHS->HasZero)) |
| 644 | (Negative ? ResLower : ResUpper) = APFloat::getInf(Sem, Negative); |
| 645 | // Finite / inf = 0 |
| 646 | if (LHS->FinitePart && RHS->HasInf) { |
| 647 | APFloat Zero = APFloat::getZero(Sem, /*Negative=*/Negative); |
| 648 | ResLower = minnum(A: ResLower, B: Zero); |
| 649 | ResUpper = maxnum(A: ResUpper, B: Zero); |
| 650 | } |
| 651 | // Finite / FiniteNonZero |
| 652 | if (LHS->FinitePart && RHSHasNonZeroFinite) { |
| 653 | assert(!RHS->FinitePart->second.isZero() && |
| 654 | "Divisor should be non-zero." ); |
| 655 | APFloat NewLower = LHS->FinitePart->first / RHS->FinitePart->second; |
| 656 | APFloat NewUpper = LHS->FinitePart->second / |
| 657 | (RHS->FinitePart->first.isZero() |
| 658 | ? APFloat::getSmallest(Sem, /*Negative=*/false) |
| 659 | : RHS->FinitePart->first); |
| 660 | if (Negative) { |
| 661 | ResLower = minnum(A: ResLower, B: -NewUpper); |
| 662 | ResUpper = maxnum(A: ResUpper, B: -NewLower); |
| 663 | } else { |
| 664 | ResLower = minnum(A: ResLower, B: NewLower); |
| 665 | ResUpper = maxnum(A: ResUpper, B: NewUpper); |
| 666 | } |
| 667 | } |
| 668 | }; |
| 669 | Update(LHSNeg, RHSNeg, /*Negative=*/false); |
| 670 | Update(LHSNeg, RHSPos, /*Negative=*/true); |
| 671 | Update(LHSPos, RHSNeg, /*Negative=*/true); |
| 672 | Update(LHSPos, RHSPos, /*Negative=*/false); |
| 673 | return ConstantFPRange(ResLower, ResUpper, ResMayBeQNaN, /*MayBeSNaN=*/false); |
| 674 | } |
| 675 | |