xref: /llvm-project/llvm/lib/IR/ConstantFPRange.cpp (revision fa824dc0dd960214865b03d8f56b18bb93e4a88b)
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/Analysis/ValueTracking.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(Lower, 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(Lower.getSemantics(), /*Negative=*/false);
80     Upper = APFloat::getInf(Upper.getSemantics(), /*Negative=*/true);
81   }
82 }
83 
84 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
85                                  bool MayBeQNaN, bool MayBeSNaN)
86     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
87   assert(&Lower.getSemantics() == &Upper.getSemantics() &&
88          "Should only use the same semantics");
89   assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form");
90   this->MayBeQNaN = MayBeQNaN;
91   this->MayBeSNaN = MayBeSNaN;
92 }
93 
94 ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) {
95   return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true),
96                          APFloat::getLargest(Sem, /*Negative=*/false),
97                          /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
98 }
99 
100 ConstantFPRange ConstantFPRange::getNaNOnly(const fltSemantics &Sem,
101                                             bool MayBeQNaN, bool MayBeSNaN) {
102   return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false),
103                          APFloat::getInf(Sem, /*Negative=*/true), MayBeQNaN,
104                          MayBeSNaN);
105 }
106 
107 ConstantFPRange
108 ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
109                                        const ConstantFPRange &Other) {
110   // TODO
111   return getFull(Other.getSemantics());
112 }
113 
114 ConstantFPRange
115 ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
116                                           const ConstantFPRange &Other) {
117   // TODO
118   return getEmpty(Other.getSemantics());
119 }
120 
121 ConstantFPRange ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred,
122                                                      const APFloat &Other) {
123   return makeAllowedFCmpRegion(Pred, ConstantFPRange(Other));
124 }
125 
126 bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
127                            const ConstantFPRange &Other) const {
128   return makeSatisfyingFCmpRegion(Pred, Other).contains(*this);
129 }
130 
131 bool ConstantFPRange::isFullSet() const {
132   return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN &&
133          MayBeSNaN;
134 }
135 
136 bool ConstantFPRange::isEmptySet() const {
137   return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN &&
138          !MayBeSNaN;
139 }
140 
141 bool ConstantFPRange::contains(const APFloat &Val) const {
142   assert(&getSemantics() == &Val.getSemantics() &&
143          "Should only use the same semantics");
144 
145   if (Val.isNaN())
146     return Val.isSignaling() ? MayBeSNaN : MayBeQNaN;
147   return strictCompare(Lower, Val) != APFloat::cmpGreaterThan &&
148          strictCompare(Val, Upper) != APFloat::cmpGreaterThan;
149 }
150 
151 bool ConstantFPRange::contains(const ConstantFPRange &CR) const {
152   assert(&getSemantics() == &CR.getSemantics() &&
153          "Should only use the same semantics");
154 
155   if (CR.MayBeQNaN && !MayBeQNaN)
156     return false;
157 
158   if (CR.MayBeSNaN && !MayBeSNaN)
159     return false;
160 
161   return strictCompare(Lower, CR.Lower) != APFloat::cmpGreaterThan &&
162          strictCompare(CR.Upper, Upper) != APFloat::cmpGreaterThan;
163 }
164 
165 const APFloat *ConstantFPRange::getSingleElement() const {
166   if (MayBeSNaN || MayBeQNaN)
167     return nullptr;
168   return Lower.bitwiseIsEqual(Upper) ? &Lower : nullptr;
169 }
170 
171 std::optional<bool> ConstantFPRange::getSignBit() const {
172   if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative())
173     return Lower.isNegative();
174   return std::nullopt;
175 }
176 
177 bool ConstantFPRange::operator==(const ConstantFPRange &CR) const {
178   if (MayBeSNaN != CR.MayBeSNaN || MayBeQNaN != CR.MayBeQNaN)
179     return false;
180   return Lower.bitwiseIsEqual(CR.Lower) && Upper.bitwiseIsEqual(CR.Upper);
181 }
182 
183 FPClassTest ConstantFPRange::classify() const {
184   uint32_t Mask = fcNone;
185   if (MayBeSNaN)
186     Mask |= fcSNan;
187   if (MayBeQNaN)
188     Mask |= fcQNan;
189   if (!isNaNOnly()) {
190     FPClassTest LowerMask = Lower.classify();
191     FPClassTest UpperMask = Upper.classify();
192     assert(LowerMask <= UpperMask && "Range is nan-only.");
193     for (uint32_t I = LowerMask; I <= UpperMask; I <<= 1)
194       Mask |= I;
195   }
196   return static_cast<FPClassTest>(Mask);
197 }
198 
199 KnownFPClass ConstantFPRange::toKnownFPClass() const {
200   KnownFPClass Result;
201   Result.KnownFPClasses = classify();
202   Result.SignBit = getSignBit();
203   return Result;
204 }
205 
206 void ConstantFPRange::print(raw_ostream &OS) const {
207   if (isFullSet())
208     OS << "full-set";
209   else if (isEmptySet())
210     OS << "empty-set";
211   else {
212     bool NaNOnly = isNaNOnly();
213     if (!NaNOnly)
214       OS << '[' << Lower << ", " << Upper << ']';
215 
216     if (MayBeSNaN || MayBeQNaN) {
217       if (!NaNOnly)
218         OS << " with ";
219       if (MayBeSNaN && MayBeQNaN)
220         OS << "NaN";
221       else if (MayBeSNaN)
222         OS << "SNaN";
223       else if (MayBeQNaN)
224         OS << "QNaN";
225     }
226   }
227 }
228 
229 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
230 LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
231 #endif
232 
233 ConstantFPRange
234 ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
235   assert(&getSemantics() == &CR.getSemantics() &&
236          "Should only use the same semantics");
237   APFloat NewLower = maxnum(Lower, CR.Lower);
238   APFloat NewUpper = minnum(Upper, CR.Upper);
239   canonicalizeRange(NewLower, NewUpper);
240   return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
241                          MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN);
242 }
243 
244 ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
245   assert(&getSemantics() == &CR.getSemantics() &&
246          "Should only use the same semantics");
247   return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
248                          MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
249 }
250