Skip to content

Commit 553fdd7

Browse files
committed
[LVI][SCCP] Add basic ConstantFPRange support
1 parent a3a253d commit 553fdd7

File tree

6 files changed

+316
-41
lines changed

6 files changed

+316
-41
lines changed

llvm/include/llvm/Analysis/ValueLattice.h

Lines changed: 195 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_ANALYSIS_VALUELATTICE_H
1010
#define LLVM_ANALYSIS_VALUELATTICE_H
1111

12+
#include "llvm/IR/ConstantFPRange.h"
1213
#include "llvm/IR/ConstantRange.h"
1314
#include "llvm/IR/Constants.h"
1415

@@ -38,6 +39,7 @@ class ValueLatticeElement {
3839
/// Transition allowed to the following states:
3940
/// constant
4041
/// constantrange_including_undef
42+
/// constantfprange_including_undef
4143
/// overdefined
4244
undef,
4345

@@ -70,6 +72,21 @@ class ValueLatticeElement {
7072
/// overdefined
7173
constantrange_including_undef,
7274

75+
/// The Value falls within this range. (Used only for floating point typed
76+
/// values.)
77+
/// Transition allowed to the following states:
78+
/// constantfprange (new range must be a superset of the existing range)
79+
/// constantfprange_including_undef
80+
/// overdefined
81+
constantfprange,
82+
83+
/// This Value falls within this range, but also may be undef.
84+
/// Merging it with other constant ranges results in
85+
/// constantfprange_including_undef.
86+
/// Transition allowed to the following states:
87+
/// overdefined
88+
constantfprange_including_undef,
89+
7390
/// We can not precisely model the dynamic values this value might take.
7491
/// No transitions are allowed after reaching overdefined.
7592
overdefined,
@@ -85,6 +102,7 @@ class ValueLatticeElement {
85102
union {
86103
Constant *ConstVal;
87104
ConstantRange Range;
105+
ConstantFPRange FPRange;
88106
};
89107

90108
/// Destroy contents of lattice value, without destructing the object.
@@ -100,6 +118,10 @@ class ValueLatticeElement {
100118
case constantrange:
101119
Range.~ConstantRange();
102120
break;
121+
case constantfprange_including_undef:
122+
case constantfprange:
123+
FPRange.~ConstantFPRange();
124+
break;
103125
};
104126
}
105127

@@ -154,6 +176,11 @@ class ValueLatticeElement {
154176
new (&Range) ConstantRange(Other.Range);
155177
NumRangeExtensions = Other.NumRangeExtensions;
156178
break;
179+
case constantfprange:
180+
case constantfprange_including_undef:
181+
new (&FPRange) ConstantFPRange(Other.FPRange);
182+
NumRangeExtensions = Other.NumRangeExtensions;
183+
break;
157184
case constant:
158185
case notconstant:
159186
ConstVal = Other.ConstVal;
@@ -173,6 +200,11 @@ class ValueLatticeElement {
173200
new (&Range) ConstantRange(std::move(Other.Range));
174201
NumRangeExtensions = Other.NumRangeExtensions;
175202
break;
203+
case constantfprange:
204+
case constantfprange_including_undef:
205+
new (&FPRange) ConstantFPRange(Other.FPRange);
206+
NumRangeExtensions = Other.NumRangeExtensions;
207+
break;
176208
case constant:
177209
case notconstant:
178210
ConstVal = Other.ConstVal;
@@ -225,6 +257,23 @@ class ValueLatticeElement {
225257
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
226258
return Res;
227259
}
260+
static ValueLatticeElement getFPRange(ConstantFPRange CR,
261+
bool MayIncludeUndef = false) {
262+
if (CR.isFullSet())
263+
return getOverdefined();
264+
265+
if (CR.isEmptySet()) {
266+
ValueLatticeElement Res;
267+
if (MayIncludeUndef)
268+
Res.markUndef();
269+
return Res;
270+
}
271+
272+
ValueLatticeElement Res;
273+
Res.markConstantFPRange(std::move(CR),
274+
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
275+
return Res;
276+
}
228277
static ValueLatticeElement getOverdefined() {
229278
ValueLatticeElement Res;
230279
Res.markOverdefined();
@@ -239,6 +288,9 @@ class ValueLatticeElement {
239288
bool isConstantRangeIncludingUndef() const {
240289
return Tag == constantrange_including_undef;
241290
}
291+
bool isConstantFPRangeIncludingUndef() const {
292+
return Tag == constantfprange_including_undef;
293+
}
242294
/// Returns true if this value is a constant range. Use \p UndefAllowed to
243295
/// exclude non-singleton constant ranges that may also be undef. Note that
244296
/// this function also returns true if the range may include undef, but only
@@ -247,6 +299,16 @@ class ValueLatticeElement {
247299
return Tag == constantrange || (Tag == constantrange_including_undef &&
248300
(UndefAllowed || Range.isSingleElement()));
249301
}
302+
/// Returns true if this value is a constant floating point range. Use \p
303+
/// UndefAllowed to exclude non-singleton constant ranges that may also be
304+
/// undef. Note that this function also returns true if the range may include
305+
/// undef, but only contains a single element. In that case, it can be
306+
/// replaced by a constant.
307+
bool isConstantFPRange(bool UndefAllowed = true) const {
308+
return Tag == constantfprange ||
309+
(Tag == constantfprange_including_undef &&
310+
(UndefAllowed || FPRange.isSingleElement()));
311+
}
250312
bool isOverdefined() const { return Tag == overdefined; }
251313

252314
Constant *getConstant() const {
@@ -269,6 +331,17 @@ class ValueLatticeElement {
269331
return Range;
270332
}
271333

334+
/// Returns the constant floating point range for this value. Use \p
335+
/// UndefAllowed to exclude non-singleton constant ranges that may also be
336+
/// undef. Note that this function also returns a range if the range may
337+
/// include undef, but only contains a single element. In that case, it can be
338+
/// replaced by a constant.
339+
const ConstantFPRange &getConstantFPRange(bool UndefAllowed = true) const {
340+
assert(isConstantFPRange(UndefAllowed) &&
341+
"Cannot get the constant-fprange of a non-constant-fprange!");
342+
return FPRange;
343+
}
344+
272345
std::optional<APInt> asConstantInteger() const {
273346
if (isConstant() && isa<ConstantInt>(getConstant())) {
274347
return cast<ConstantInt>(getConstant())->getValue();
@@ -278,6 +351,15 @@ class ValueLatticeElement {
278351
return std::nullopt;
279352
}
280353

354+
std::optional<APFloat> asConstantFP() const {
355+
if (isConstant() && isa<ConstantFP>(getConstant())) {
356+
return cast<ConstantFP>(getConstant())->getValue();
357+
} else if (isConstantFPRange() && getConstantFPRange().isSingleElement()) {
358+
return *getConstantFPRange().getSingleElement();
359+
}
360+
return std::nullopt;
361+
}
362+
281363
ConstantRange asConstantRange(unsigned BW, bool UndefAllowed = false) const {
282364
if (isConstantRange(UndefAllowed))
283365
return getConstantRange();
@@ -288,11 +370,28 @@ class ValueLatticeElement {
288370
return ConstantRange::getFull(BW);
289371
}
290372

373+
ConstantFPRange asConstantFPRange(const fltSemantics &Sem,
374+
bool UndefAllowed = false) const {
375+
if (isConstantFPRange(UndefAllowed))
376+
return getConstantFPRange();
377+
if (isConstant())
378+
return getConstant()->toConstantFPRange();
379+
if (isUnknown())
380+
return ConstantFPRange::getEmpty(Sem);
381+
return ConstantFPRange::getFull(Sem);
382+
}
383+
291384
ConstantRange asConstantRange(Type *Ty, bool UndefAllowed = false) const {
292385
assert(Ty->isIntOrIntVectorTy() && "Must be integer type");
293386
return asConstantRange(Ty->getScalarSizeInBits(), UndefAllowed);
294387
}
295388

389+
ConstantFPRange asConstantFPRange(Type *Ty, bool UndefAllowed = false) const {
390+
assert(Ty->isFPOrFPVectorTy() && "Must be floating point type");
391+
return asConstantFPRange(Ty->getScalarType()->getFltSemantics(),
392+
UndefAllowed);
393+
}
394+
296395
bool markOverdefined() {
297396
if (isOverdefined())
298397
return false;
@@ -394,6 +493,51 @@ class ValueLatticeElement {
394493
return true;
395494
}
396495

496+
/// Mark the object as constant floating point range with \p NewR. If the
497+
/// object is already a constant range, nothing changes if the existing range
498+
/// is equal to \p NewR and the tag. Otherwise \p NewR must be a superset of
499+
/// the existing range or the object must be undef. The tag is set to
500+
/// constant_range_including_undef if either the existing value or the new
501+
/// range may include undef.
502+
bool markConstantFPRange(ConstantFPRange NewR,
503+
MergeOptions Opts = MergeOptions()) {
504+
assert(!NewR.isEmptySet() && "should only be called for non-empty sets");
505+
506+
if (NewR.isFullSet())
507+
return markOverdefined();
508+
509+
ValueLatticeElementTy OldTag = Tag;
510+
ValueLatticeElementTy NewTag =
511+
(isUndef() || isConstantFPRangeIncludingUndef() || Opts.MayIncludeUndef)
512+
? constantfprange_including_undef
513+
: constantfprange;
514+
if (isConstantFPRange()) {
515+
Tag = NewTag;
516+
if (getConstantFPRange() == NewR)
517+
return Tag != OldTag;
518+
519+
// Simple form of widening. If a range is extended multiple times, go to
520+
// overdefined.
521+
if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps)
522+
return markOverdefined();
523+
524+
assert(NewR.contains(getConstantFPRange()) &&
525+
"Existing range must be a subset of NewR");
526+
FPRange = std::move(NewR);
527+
return true;
528+
}
529+
530+
assert(isUnknown() || isUndef() || isConstant());
531+
assert(
532+
(!isConstant() || NewR.contains(getConstant()->toConstantFPRange())) &&
533+
"Constant must be subset of new range");
534+
535+
NumRangeExtensions = 0;
536+
Tag = NewTag;
537+
new (&FPRange) ConstantFPRange(std::move(NewR));
538+
return true;
539+
}
540+
397541
/// Updates this object to approximate both this object and RHS. Returns
398542
/// true if this object has been changed.
399543
bool mergeIn(const ValueLatticeElement &RHS,
@@ -414,6 +558,9 @@ class ValueLatticeElement {
414558
if (RHS.isConstantRange())
415559
return markConstantRange(RHS.getConstantRange(true),
416560
Opts.setMayIncludeUndef());
561+
if (RHS.isConstantFPRange())
562+
return markConstantFPRange(RHS.getConstantFPRange(true),
563+
Opts.setMayIncludeUndef());
417564
return markOverdefined();
418565
}
419566

@@ -428,15 +575,26 @@ class ValueLatticeElement {
428575
return false;
429576
if (RHS.isUndef())
430577
return false;
431-
// If the constant is a vector of integers, try to treat it as a range.
432-
if (getConstant()->getType()->isVectorTy() &&
433-
getConstant()->getType()->getScalarType()->isIntegerTy()) {
434-
ConstantRange L = getConstant()->toConstantRange();
435-
ConstantRange NewR = L.unionWith(
436-
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
437-
return markConstantRange(
438-
std::move(NewR),
439-
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
578+
// If the constant is a vector of integers/floating point values, try to
579+
// treat it as a range.
580+
if (getConstant()->getType()->isVectorTy()) {
581+
Type *ScalarTy = getConstant()->getType()->getScalarType();
582+
if (ScalarTy->isIntegerTy()) {
583+
ConstantRange L = getConstant()->toConstantRange();
584+
ConstantRange NewR = L.unionWith(
585+
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
586+
return markConstantRange(
587+
std::move(NewR),
588+
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
589+
}
590+
if (ScalarTy->isFloatingPointTy()) {
591+
ConstantFPRange L = getConstant()->toConstantFPRange();
592+
ConstantFPRange NewR = L.unionWith(
593+
RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
594+
return markConstantFPRange(
595+
std::move(NewR),
596+
Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
597+
}
440598
}
441599
markOverdefined();
442600
return true;
@@ -450,18 +608,35 @@ class ValueLatticeElement {
450608
}
451609

452610
auto OldTag = Tag;
453-
assert(isConstantRange() && "New ValueLattice type?");
454-
if (RHS.isUndef()) {
455-
Tag = constantrange_including_undef;
456-
return OldTag != Tag;
611+
if (isConstantRange()) {
612+
if (RHS.isUndef()) {
613+
Tag = constantrange_including_undef;
614+
return OldTag != Tag;
615+
}
616+
617+
const ConstantRange &L = getConstantRange();
618+
ConstantRange NewR = L.unionWith(
619+
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
620+
return markConstantRange(
621+
std::move(NewR),
622+
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
457623
}
458624

459-
const ConstantRange &L = getConstantRange();
460-
ConstantRange NewR = L.unionWith(
461-
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
462-
return markConstantRange(
463-
std::move(NewR),
464-
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
625+
if (isConstantFPRange()) {
626+
if (RHS.isUndef()) {
627+
Tag = constantfprange_including_undef;
628+
return OldTag != Tag;
629+
}
630+
631+
const ConstantFPRange &L = getConstantFPRange();
632+
ConstantFPRange NewR = L.unionWith(
633+
RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
634+
return markConstantFPRange(
635+
std::move(NewR),
636+
Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
637+
} else {
638+
llvm_unreachable("New ValueLattice type?");
639+
}
465640
}
466641

467642
// Compares this symbolic value with Other using Pred and returns either
@@ -492,7 +667,7 @@ class ValueLatticeElement {
492667
void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; }
493668
};
494669

495-
static_assert(sizeof(ValueLatticeElement) <= 40,
670+
static_assert(sizeof(ValueLatticeElement) <= 80,
496671
"size of ValueLatticeElement changed unexpectedly");
497672

498673
raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val);

llvm/include/llvm/IR/Constant.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace llvm {
2121

2222
class ConstantRange;
23+
class ConstantFPRange;
2324
class APInt;
2425

2526
/// This is an important base class in LLVM. It provides the common facilities
@@ -159,6 +160,11 @@ class Constant : public User {
159160
/// range is the union over the element ranges. Poison elements are ignored.
160161
ConstantRange toConstantRange() const;
161162

163+
/// Convert constant to an approximate constant floating point range. For
164+
/// vectors, the range is the union over the element ranges. Poison elements
165+
/// are ignored.
166+
ConstantFPRange toConstantFPRange() const;
167+
162168
/// Called if some element of this constant is no longer valid.
163169
/// At this point only other constants may be on the use_list for this
164170
/// constant. Any constants on our Use list must also be destroy'd. The

0 commit comments

Comments
 (0)