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
16using namespace llvm;
17
18void 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
26void 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
34bool ConstantFPRange::isNaNOnly() const {
35 return Lower.isPosInfinity() && Upper.isNegInfinity();
36}
37
38ConstantFPRange::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
46ConstantFPRange::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.
61static 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
72static bool isNonCanonicalEmptySet(const APFloat &Lower, const APFloat &Upper) {
73 return strictCompare(LHS: Lower, RHS: Upper) == APFloat::cmpGreaterThan &&
74 !(Lower.isInfinity() && Upper.isInfinity());
75}
76
77static 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
84ConstantFPRange::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
93ConstantFPRange 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
99ConstantFPRange 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
106ConstantFPRange 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
113static bool fcmpPredExcludesEqual(FCmpInst::Predicate Pred) {
114 return !(Pred & FCmpInst::FCMP_OEQ);
115}
116
117/// Return [-inf, V) or [-inf, V]
118static 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]
130static 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.
142static 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
157static 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
164ConstantFPRange
165ConstantFPRange::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
222ConstantFPRange
223ConstantFPRange::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
269std::optional<ConstantFPRange>
270ConstantFPRange::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
278bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
279 const ConstantFPRange &Other) const {
280 return makeSatisfyingFCmpRegion(Pred, Other).contains(CR: *this);
281}
282
283bool ConstantFPRange::isFullSet() const {
284 return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN &&
285 MayBeSNaN;
286}
287
288bool ConstantFPRange::isEmptySet() const {
289 return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN &&
290 !MayBeSNaN;
291}
292
293bool 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
303bool 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
317const APFloat *ConstantFPRange::getSingleElement(bool ExcludesNaN) const {
318 if (!ExcludesNaN && (MayBeSNaN || MayBeQNaN))
319 return nullptr;
320 return Lower.bitwiseIsEqual(RHS: Upper) ? &Lower : nullptr;
321}
322
323std::optional<bool> ConstantFPRange::getSignBit() const {
324 if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative())
325 return Lower.isNegative();
326 return std::nullopt;
327}
328
329bool 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
337FPClassTest 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
353void 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)
377LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
378#endif
379
380ConstantFPRange
381ConstantFPRange::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
391ConstantFPRange 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
398ConstantFPRange 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
414ConstantFPRange 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.
419static 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
435ConstantFPRange 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
448ConstantFPRange 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
465ConstantFPRange 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
506ConstantFPRange ConstantFPRange::sub(const ConstantFPRange &Other) const {
507 // fsub X, Y = fadd X, (fneg Y)
508 return add(Other: Other.negate());
509}
510
511void 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.
533struct 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.
555static 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
572ConstantFPRange 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
614ConstantFPRange 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