Skip to content

[LVI][SCCP][CVP] Add basic ConstantFPRange support #111544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 195 additions & 20 deletions llvm/include/llvm/Analysis/ValueLattice.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_ANALYSIS_VALUELATTICE_H
#define LLVM_ANALYSIS_VALUELATTICE_H

#include "llvm/IR/ConstantFPRange.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"

Expand Down Expand Up @@ -38,6 +39,7 @@ class ValueLatticeElement {
/// Transition allowed to the following states:
/// constant
/// constantrange_including_undef
/// constantfprange_including_undef
/// overdefined
undef,

Expand Down Expand Up @@ -70,6 +72,21 @@ class ValueLatticeElement {
/// overdefined
constantrange_including_undef,

/// The Value falls within this range. (Used only for floating point typed
/// values.)
/// Transition allowed to the following states:
/// constantfprange (new range must be a superset of the existing range)
/// constantfprange_including_undef
/// overdefined
constantfprange,

/// This Value falls within this range, but also may be undef.
/// Merging it with other constant ranges results in
/// constantfprange_including_undef.
/// Transition allowed to the following states:
/// overdefined
constantfprange_including_undef,

/// We can not precisely model the dynamic values this value might take.
/// No transitions are allowed after reaching overdefined.
overdefined,
Expand All @@ -85,6 +102,7 @@ class ValueLatticeElement {
union {
Constant *ConstVal;
ConstantRange Range;
ConstantFPRange FPRange;
};

/// Destroy contents of lattice value, without destructing the object.
Expand All @@ -100,6 +118,10 @@ class ValueLatticeElement {
case constantrange:
Range.~ConstantRange();
break;
case constantfprange_including_undef:
case constantfprange:
FPRange.~ConstantFPRange();
break;
};
}

Expand Down Expand Up @@ -154,6 +176,11 @@ class ValueLatticeElement {
new (&Range) ConstantRange(Other.Range);
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constantfprange:
case constantfprange_including_undef:
new (&FPRange) ConstantFPRange(Other.FPRange);
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constant:
case notconstant:
ConstVal = Other.ConstVal;
Expand All @@ -173,6 +200,11 @@ class ValueLatticeElement {
new (&Range) ConstantRange(std::move(Other.Range));
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constantfprange:
case constantfprange_including_undef:
new (&FPRange) ConstantFPRange(std::move(Other.FPRange));
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constant:
case notconstant:
ConstVal = Other.ConstVal;
Expand Down Expand Up @@ -225,6 +257,23 @@ class ValueLatticeElement {
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
return Res;
}
static ValueLatticeElement getFPRange(ConstantFPRange CR,
bool MayIncludeUndef = false) {
if (CR.isFullSet())
return getOverdefined();

if (CR.isEmptySet()) {
ValueLatticeElement Res;
if (MayIncludeUndef)
Res.markUndef();
return Res;
}

ValueLatticeElement Res;
Res.markConstantFPRange(std::move(CR),
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
return Res;
}
static ValueLatticeElement getOverdefined() {
ValueLatticeElement Res;
Res.markOverdefined();
Expand All @@ -239,6 +288,9 @@ class ValueLatticeElement {
bool isConstantRangeIncludingUndef() const {
return Tag == constantrange_including_undef;
}
bool isConstantFPRangeIncludingUndef() const {
return Tag == constantfprange_including_undef;
}
/// Returns true if this value is a constant range. Use \p UndefAllowed to
/// exclude non-singleton constant ranges that may also be undef. Note that
/// this function also returns true if the range may include undef, but only
Expand All @@ -247,6 +299,16 @@ class ValueLatticeElement {
return Tag == constantrange || (Tag == constantrange_including_undef &&
(UndefAllowed || Range.isSingleElement()));
}
/// Returns true if this value is a constant floating point range. Use \p
/// UndefAllowed to exclude non-singleton constant ranges that may also be
/// undef. Note that this function also returns true if the range may include
/// undef, but only contains a single element. In that case, it can be
/// replaced by a constant.
bool isConstantFPRange(bool UndefAllowed = true) const {
return Tag == constantfprange ||
(Tag == constantfprange_including_undef &&
(UndefAllowed || FPRange.isSingleElement()));
}
bool isOverdefined() const { return Tag == overdefined; }

Constant *getConstant() const {
Expand All @@ -269,6 +331,17 @@ class ValueLatticeElement {
return Range;
}

/// Returns the constant floating point range for this value. Use \p
/// UndefAllowed to exclude non-singleton constant ranges that may also be
/// undef. Note that this function also returns a range if the range may
/// include undef, but only contains a single element. In that case, it can be
/// replaced by a constant.
const ConstantFPRange &getConstantFPRange(bool UndefAllowed = true) const {
assert(isConstantFPRange(UndefAllowed) &&
"Cannot get the constant-fprange of a non-constant-fprange!");
return FPRange;
}

std::optional<APInt> asConstantInteger() const {
if (isConstant() && isa<ConstantInt>(getConstant())) {
return cast<ConstantInt>(getConstant())->getValue();
Expand All @@ -278,6 +351,15 @@ class ValueLatticeElement {
return std::nullopt;
}

std::optional<APFloat> asConstantFP() const {
if (isConstant() && isa<ConstantFP>(getConstant())) {
return cast<ConstantFP>(getConstant())->getValue();
} else if (isConstantFPRange() && getConstantFPRange().isSingleElement()) {
return *getConstantFPRange().getSingleElement();
}
return std::nullopt;
}

ConstantRange asConstantRange(unsigned BW, bool UndefAllowed = false) const {
if (isConstantRange(UndefAllowed))
return getConstantRange();
Expand All @@ -288,11 +370,28 @@ class ValueLatticeElement {
return ConstantRange::getFull(BW);
}

ConstantFPRange asConstantFPRange(const fltSemantics &Sem,
bool UndefAllowed = false) const {
if (isConstantFPRange(UndefAllowed))
return getConstantFPRange();
if (isConstant())
return getConstant()->toConstantFPRange();
if (isUnknown())
return ConstantFPRange::getEmpty(Sem);
return ConstantFPRange::getFull(Sem);
}

ConstantRange asConstantRange(Type *Ty, bool UndefAllowed = false) const {
assert(Ty->isIntOrIntVectorTy() && "Must be integer type");
return asConstantRange(Ty->getScalarSizeInBits(), UndefAllowed);
}

ConstantFPRange asConstantFPRange(Type *Ty, bool UndefAllowed = false) const {
assert(Ty->isFPOrFPVectorTy() && "Must be floating point type");
return asConstantFPRange(Ty->getScalarType()->getFltSemantics(),
UndefAllowed);
}

bool markOverdefined() {
if (isOverdefined())
return false;
Expand Down Expand Up @@ -394,6 +493,51 @@ class ValueLatticeElement {
return true;
}

/// Mark the object as constant floating point range with \p NewR. If the
/// object is already a constant range, nothing changes if the existing range
/// is equal to \p NewR and the tag. Otherwise \p NewR must be a superset of
/// the existing range or the object must be undef. The tag is set to
/// constant_range_including_undef if either the existing value or the new
/// range may include undef.
bool markConstantFPRange(ConstantFPRange NewR,
MergeOptions Opts = MergeOptions()) {
assert(!NewR.isEmptySet() && "should only be called for non-empty sets");

if (NewR.isFullSet())
return markOverdefined();

ValueLatticeElementTy OldTag = Tag;
ValueLatticeElementTy NewTag =
(isUndef() || isConstantFPRangeIncludingUndef() || Opts.MayIncludeUndef)
? constantfprange_including_undef
: constantfprange;
if (isConstantFPRange()) {
Tag = NewTag;
if (getConstantFPRange() == NewR)
return Tag != OldTag;

// Simple form of widening. If a range is extended multiple times, go to
// overdefined.
if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps)
return markOverdefined();

assert(NewR.contains(getConstantFPRange()) &&
"Existing range must be a subset of NewR");
FPRange = std::move(NewR);
return true;
}

assert(isUnknown() || isUndef() || isConstant());
assert(
(!isConstant() || NewR.contains(getConstant()->toConstantFPRange())) &&
"Constant must be subset of new range");

NumRangeExtensions = 0;
Tag = NewTag;
new (&FPRange) ConstantFPRange(std::move(NewR));
return true;
}

/// Updates this object to approximate both this object and RHS. Returns
/// true if this object has been changed.
bool mergeIn(const ValueLatticeElement &RHS,
Expand All @@ -414,6 +558,9 @@ class ValueLatticeElement {
if (RHS.isConstantRange())
return markConstantRange(RHS.getConstantRange(true),
Opts.setMayIncludeUndef());
if (RHS.isConstantFPRange())
return markConstantFPRange(RHS.getConstantFPRange(true),
Opts.setMayIncludeUndef());
return markOverdefined();
}

Expand All @@ -428,15 +575,26 @@ class ValueLatticeElement {
return false;
if (RHS.isUndef())
return false;
// If the constant is a vector of integers, try to treat it as a range.
if (getConstant()->getType()->isVectorTy() &&
getConstant()->getType()->getScalarType()->isIntegerTy()) {
ConstantRange L = getConstant()->toConstantRange();
ConstantRange NewR = L.unionWith(
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
return markConstantRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
// If the constant is a vector of integers/floating point values, try to
// treat it as a range.
if (getConstant()->getType()->isVectorTy()) {
Type *ScalarTy = getConstant()->getType()->getScalarType();
if (ScalarTy->isIntegerTy()) {
ConstantRange L = getConstant()->toConstantRange();
ConstantRange NewR = L.unionWith(
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
return markConstantRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
}
if (ScalarTy->isFloatingPointTy()) {
ConstantFPRange L = getConstant()->toConstantFPRange();
ConstantFPRange NewR = L.unionWith(
RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
return markConstantFPRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
}
}
markOverdefined();
return true;
Expand All @@ -450,18 +608,35 @@ class ValueLatticeElement {
}

auto OldTag = Tag;
assert(isConstantRange() && "New ValueLattice type?");
if (RHS.isUndef()) {
Tag = constantrange_including_undef;
return OldTag != Tag;
if (isConstantRange()) {
if (RHS.isUndef()) {
Tag = constantrange_including_undef;
return OldTag != Tag;
}

const ConstantRange &L = getConstantRange();
ConstantRange NewR = L.unionWith(
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
return markConstantRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
}

const ConstantRange &L = getConstantRange();
ConstantRange NewR = L.unionWith(
RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
return markConstantRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
if (isConstantFPRange()) {
if (RHS.isUndef()) {
Tag = constantfprange_including_undef;
return OldTag != Tag;
}

const ConstantFPRange &L = getConstantFPRange();
ConstantFPRange NewR = L.unionWith(
RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
return markConstantFPRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
} else {
llvm_unreachable("New ValueLattice type?");
}
}

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

static_assert(sizeof(ValueLatticeElement) <= 40,
static_assert(sizeof(ValueLatticeElement) <= 64,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks better after #111641. But I believe we can further reduce the size of APFloat from 24 to 16.

"size of ValueLatticeElement changed unexpectedly");

raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val);
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/Constant.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
namespace llvm {

class ConstantRange;
class ConstantFPRange;
class APInt;

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

/// Convert constant to an approximate constant floating point range. For
/// vectors, the range is the union over the element ranges. Poison elements
/// are ignored.
ConstantFPRange toConstantFPRange() const;

/// Called if some element of this constant is no longer valid.
/// At this point only other constants may be on the use_list for this
/// constant. Any constants on our Use list must also be destroy'd. The
Expand Down
Loading
Loading