xref: /llvm-project/llvm/lib/IR/ConstantFPRange.cpp (revision a3a253d3c7780977077dd46493917b1949c0166d)
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/Support/Debug.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include <cassert>
14 
15 using namespace llvm;
16 
17 void ConstantFPRange::makeEmpty() {
18   auto &Sem = Lower.getSemantics();
19   Lower = APFloat::getInf(Sem, /*Negative=*/false);
20   Upper = APFloat::getInf(Sem, /*Negative=*/true);
21   MayBeQNaN = false;
22   MayBeSNaN = false;
23 }
24 
25 void ConstantFPRange::makeFull() {
26   auto &Sem = Lower.getSemantics();
27   Lower = APFloat::getInf(Sem, /*Negative=*/true);
28   Upper = APFloat::getInf(Sem, /*Negative=*/false);
29   MayBeQNaN = true;
30   MayBeSNaN = true;
31 }
32 
33 bool ConstantFPRange::isNaNOnly() const {
34   return Lower.isPosInfinity() && Upper.isNegInfinity();
35 }
36 
37 ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet)
38     : Lower(Sem, APFloat::uninitialized), Upper(Sem, APFloat::uninitialized) {
39   Lower = APFloat::getInf(Sem, /*Negative=*/IsFullSet);
40   Upper = APFloat::getInf(Sem, /*Negative=*/!IsFullSet);
41   MayBeQNaN = IsFullSet;
42   MayBeSNaN = IsFullSet;
43 }
44 
45 ConstantFPRange::ConstantFPRange(const APFloat &Value)
46     : Lower(Value.getSemantics(), APFloat::uninitialized),
47       Upper(Value.getSemantics(), APFloat::uninitialized) {
48   if (Value.isNaN()) {
49     makeEmpty();
50     bool IsSNaN = Value.isSignaling();
51     MayBeQNaN = !IsSNaN;
52     MayBeSNaN = IsSNaN;
53   } else {
54     Lower = Upper = Value;
55     MayBeQNaN = MayBeSNaN = false;
56   }
57 }
58 
59 // We treat that -0 is less than 0 here.
60 static APFloat::cmpResult strictCompare(const APFloat &LHS,
61                                         const APFloat &RHS) {
62   assert(!LHS.isNaN() && !RHS.isNaN() && "Unordered compare");
63   if (LHS.isZero() && RHS.isZero()) {
64     if (LHS.isNegative() == RHS.isNegative())
65       return APFloat::cmpEqual;
66     return LHS.isNegative() ? APFloat::cmpLessThan : APFloat::cmpGreaterThan;
67   }
68   return LHS.compare(RHS);
69 }
70 
71 static bool isNonCanonicalEmptySet(const APFloat &Lower, const APFloat &Upper) {
72   return strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
73          !(Lower.isInfinity() && Upper.isInfinity());
74 }
75 
76 static void canonicalizeRange(APFloat &Lower, APFloat &Upper) {
77   if (isNonCanonicalEmptySet(Lower, Upper)) {
78     Lower = APFloat::getInf(Lower.getSemantics(), /*Negative=*/false);
79     Upper = APFloat::getInf(Upper.getSemantics(), /*Negative=*/true);
80   }
81 }
82 
83 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
84                                  bool MayBeQNaNVal, bool MayBeSNaNVal)
85     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)),
86       MayBeQNaN(MayBeQNaNVal), MayBeSNaN(MayBeSNaNVal) {
87   assert(&Lower.getSemantics() == &Upper.getSemantics() &&
88          "Should only use the same semantics");
89   assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form");
90 }
91 
92 ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) {
93   return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true),
94                          APFloat::getLargest(Sem, /*Negative=*/false),
95                          /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
96 }
97 
98 ConstantFPRange ConstantFPRange::getNaNOnly(const fltSemantics &Sem,
99                                             bool MayBeQNaN, bool MayBeSNaN) {
100   return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false),
101                          APFloat::getInf(Sem, /*Negative=*/true), MayBeQNaN,
102                          MayBeSNaN);
103 }
104 
105 ConstantFPRange ConstantFPRange::getNonNaN(const fltSemantics &Sem) {
106   return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true),
107                          APFloat::getInf(Sem, /*Negative=*/false),
108                          /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
109 }
110 
111 /// Return true for ULT/UGT/OLT/OGT
112 static bool fcmpPredExcludesEqual(FCmpInst::Predicate Pred) {
113   return !(Pred & FCmpInst::FCMP_OEQ);
114 }
115 
116 /// Return [-inf, V) or [-inf, V]
117 static ConstantFPRange makeLessThan(APFloat V, FCmpInst::Predicate Pred) {
118   const fltSemantics &Sem = V.getSemantics();
119   if (fcmpPredExcludesEqual(Pred)) {
120     if (V.isNegInfinity())
121       return ConstantFPRange::getEmpty(Sem);
122     V.next(/*nextDown=*/true);
123   }
124   return ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
125                                     std::move(V));
126 }
127 
128 /// Return (V, +inf] or [V, +inf]
129 static ConstantFPRange makeGreaterThan(APFloat V, FCmpInst::Predicate Pred) {
130   const fltSemantics &Sem = V.getSemantics();
131   if (fcmpPredExcludesEqual(Pred)) {
132     if (V.isPosInfinity())
133       return ConstantFPRange::getEmpty(Sem);
134     V.next(/*nextDown=*/false);
135   }
136   return ConstantFPRange::getNonNaN(std::move(V),
137                                     APFloat::getInf(Sem, /*Negative=*/false));
138 }
139 
140 /// Make sure that +0/-0 are both included in the range.
141 static ConstantFPRange extendZeroIfEqual(const ConstantFPRange &CR,
142                                          FCmpInst::Predicate Pred) {
143   if (fcmpPredExcludesEqual(Pred))
144     return CR;
145 
146   APFloat Lower = CR.getLower();
147   APFloat Upper = CR.getUpper();
148   if (Lower.isPosZero())
149     Lower = APFloat::getZero(Lower.getSemantics(), /*Negative=*/true);
150   if (Upper.isNegZero())
151     Upper = APFloat::getZero(Upper.getSemantics(), /*Negative=*/false);
152   return ConstantFPRange(std::move(Lower), std::move(Upper), CR.containsQNaN(),
153                          CR.containsSNaN());
154 }
155 
156 static ConstantFPRange setNaNField(const ConstantFPRange &CR,
157                                    FCmpInst::Predicate Pred) {
158   bool ContainsNaN = FCmpInst::isUnordered(Pred);
159   return ConstantFPRange(CR.getLower(), CR.getUpper(),
160                          /*MayBeQNaN=*/ContainsNaN, /*MayBeSNaN=*/ContainsNaN);
161 }
162 
163 ConstantFPRange
164 ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
165                                        const ConstantFPRange &Other) {
166   if (Other.isEmptySet())
167     return Other;
168   if (Other.containsNaN() && FCmpInst::isUnordered(Pred))
169     return getFull(Other.getSemantics());
170   if (Other.isNaNOnly() && FCmpInst::isOrdered(Pred))
171     return getEmpty(Other.getSemantics());
172 
173   switch (Pred) {
174   case FCmpInst::FCMP_TRUE:
175     return getFull(Other.getSemantics());
176   case FCmpInst::FCMP_FALSE:
177     return getEmpty(Other.getSemantics());
178   case FCmpInst::FCMP_ORD:
179     return getNonNaN(Other.getSemantics());
180   case FCmpInst::FCMP_UNO:
181     return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
182                       /*MayBeSNaN=*/true);
183   case FCmpInst::FCMP_OEQ:
184   case FCmpInst::FCMP_UEQ:
185     return setNaNField(extendZeroIfEqual(Other, Pred), Pred);
186   case FCmpInst::FCMP_ONE:
187   case FCmpInst::FCMP_UNE:
188     if (const APFloat *SingleElement =
189             Other.getSingleElement(/*ExcludesNaN=*/true)) {
190       const fltSemantics &Sem = SingleElement->getSemantics();
191       if (SingleElement->isPosInfinity())
192         return setNaNField(
193             getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
194                       APFloat::getLargest(Sem, /*Negative=*/false)),
195             Pred);
196       if (SingleElement->isNegInfinity())
197         return setNaNField(
198             getNonNaN(APFloat::getLargest(Sem, /*Negative=*/true),
199                       APFloat::getInf(Sem, /*Negative=*/false)),
200             Pred);
201     }
202     return Pred == FCmpInst::FCMP_ONE ? getNonNaN(Other.getSemantics())
203                                       : getFull(Other.getSemantics());
204   case FCmpInst::FCMP_OLT:
205   case FCmpInst::FCMP_OLE:
206   case FCmpInst::FCMP_ULT:
207   case FCmpInst::FCMP_ULE:
208     return setNaNField(
209         extendZeroIfEqual(makeLessThan(Other.getUpper(), Pred), Pred), Pred);
210   case FCmpInst::FCMP_OGT:
211   case FCmpInst::FCMP_OGE:
212   case FCmpInst::FCMP_UGT:
213   case FCmpInst::FCMP_UGE:
214     return setNaNField(
215         extendZeroIfEqual(makeGreaterThan(Other.getLower(), Pred), Pred), Pred);
216   default:
217     llvm_unreachable("Unexpected predicate");
218   }
219 }
220 
221 ConstantFPRange
222 ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
223                                           const ConstantFPRange &Other) {
224   if (Other.isEmptySet())
225     return getFull(Other.getSemantics());
226   if (Other.containsNaN() && FCmpInst::isOrdered(Pred))
227     return getEmpty(Other.getSemantics());
228   if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred))
229     return getFull(Other.getSemantics());
230 
231   switch (Pred) {
232   case FCmpInst::FCMP_TRUE:
233     return getFull(Other.getSemantics());
234   case FCmpInst::FCMP_FALSE:
235     return getEmpty(Other.getSemantics());
236   case FCmpInst::FCMP_ORD:
237     return getNonNaN(Other.getSemantics());
238   case FCmpInst::FCMP_UNO:
239     return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
240                       /*MayBeSNaN=*/true);
241   case FCmpInst::FCMP_OEQ:
242   case FCmpInst::FCMP_UEQ:
243     return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) ||
244                                ((Other.classify() & ~fcNan) == fcZero)
245                            ? extendZeroIfEqual(Other, Pred)
246                            : getEmpty(Other.getSemantics()),
247                        Pred);
248   case FCmpInst::FCMP_ONE:
249   case FCmpInst::FCMP_UNE:
250     return getEmpty(Other.getSemantics());
251   case FCmpInst::FCMP_OLT:
252   case FCmpInst::FCMP_OLE:
253   case FCmpInst::FCMP_ULT:
254   case FCmpInst::FCMP_ULE:
255     return setNaNField(
256         extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred);
257   case FCmpInst::FCMP_OGT:
258   case FCmpInst::FCMP_OGE:
259   case FCmpInst::FCMP_UGT:
260   case FCmpInst::FCMP_UGE:
261     return setNaNField(
262         extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred);
263   default:
264     llvm_unreachable("Unexpected predicate");
265   }
266 }
267 
268 std::optional<ConstantFPRange>
269 ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred,
270                                      const APFloat &Other) {
271   if ((Pred == FCmpInst::FCMP_UNE || Pred == FCmpInst::FCMP_ONE) &&
272       !Other.isNaN())
273     return std::nullopt;
274   return makeSatisfyingFCmpRegion(Pred, ConstantFPRange(Other));
275 }
276 
277 bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
278                            const ConstantFPRange &Other) const {
279   return makeSatisfyingFCmpRegion(Pred, Other).contains(*this);
280 }
281 
282 bool ConstantFPRange::isFullSet() const {
283   return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN &&
284          MayBeSNaN;
285 }
286 
287 bool ConstantFPRange::isEmptySet() const {
288   return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN &&
289          !MayBeSNaN;
290 }
291 
292 bool ConstantFPRange::contains(const APFloat &Val) const {
293   assert(&getSemantics() == &Val.getSemantics() &&
294          "Should only use the same semantics");
295 
296   if (Val.isNaN())
297     return Val.isSignaling() ? MayBeSNaN : MayBeQNaN;
298   return strictCompare(Lower, Val) != APFloat::cmpGreaterThan &&
299          strictCompare(Val, Upper) != APFloat::cmpGreaterThan;
300 }
301 
302 bool ConstantFPRange::contains(const ConstantFPRange &CR) const {
303   assert(&getSemantics() == &CR.getSemantics() &&
304          "Should only use the same semantics");
305 
306   if (CR.MayBeQNaN && !MayBeQNaN)
307     return false;
308 
309   if (CR.MayBeSNaN && !MayBeSNaN)
310     return false;
311 
312   return strictCompare(Lower, CR.Lower) != APFloat::cmpGreaterThan &&
313          strictCompare(CR.Upper, Upper) != APFloat::cmpGreaterThan;
314 }
315 
316 const APFloat *ConstantFPRange::getSingleElement(bool ExcludesNaN) const {
317   if (!ExcludesNaN && (MayBeSNaN || MayBeQNaN))
318     return nullptr;
319   return Lower.bitwiseIsEqual(Upper) ? &Lower : nullptr;
320 }
321 
322 std::optional<bool> ConstantFPRange::getSignBit() const {
323   if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative())
324     return Lower.isNegative();
325   return std::nullopt;
326 }
327 
328 bool ConstantFPRange::operator==(const ConstantFPRange &CR) const {
329   if (MayBeSNaN != CR.MayBeSNaN || MayBeQNaN != CR.MayBeQNaN)
330     return false;
331   return Lower.bitwiseIsEqual(CR.Lower) && Upper.bitwiseIsEqual(CR.Upper);
332 }
333 
334 FPClassTest ConstantFPRange::classify() const {
335   uint32_t Mask = fcNone;
336   if (MayBeSNaN)
337     Mask |= fcSNan;
338   if (MayBeQNaN)
339     Mask |= fcQNan;
340   if (!isNaNOnly()) {
341     FPClassTest LowerMask = Lower.classify();
342     FPClassTest UpperMask = Upper.classify();
343     assert(LowerMask <= UpperMask && "Range is nan-only.");
344     // Set all bits from log2(LowerMask) to log2(UpperMask).
345     Mask |= (UpperMask << 1) - LowerMask;
346   }
347   return static_cast<FPClassTest>(Mask);
348 }
349 
350 void ConstantFPRange::print(raw_ostream &OS) const {
351   if (isFullSet())
352     OS << "full-set";
353   else if (isEmptySet())
354     OS << "empty-set";
355   else {
356     bool NaNOnly = isNaNOnly();
357     if (!NaNOnly)
358       OS << '[' << Lower << ", " << Upper << ']';
359 
360     if (MayBeSNaN || MayBeQNaN) {
361       if (!NaNOnly)
362         OS << " with ";
363       if (MayBeSNaN && MayBeQNaN)
364         OS << "NaN";
365       else if (MayBeSNaN)
366         OS << "SNaN";
367       else if (MayBeQNaN)
368         OS << "QNaN";
369     }
370   }
371 }
372 
373 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
374 LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
375 #endif
376 
377 ConstantFPRange
378 ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
379   assert(&getSemantics() == &CR.getSemantics() &&
380          "Should only use the same semantics");
381   APFloat NewLower = maxnum(Lower, CR.Lower);
382   APFloat NewUpper = minnum(Upper, CR.Upper);
383   canonicalizeRange(NewLower, NewUpper);
384   return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
385                          MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN);
386 }
387 
388 ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
389   assert(&getSemantics() == &CR.getSemantics() &&
390          "Should only use the same semantics");
391   return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
392                          MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
393 }
394