Skip to content
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

[IR] Add new Range attribute using new ConstantRange Attribute type #83171

Merged
merged 4 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
17 changes: 17 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,23 @@ Currently, only the following parameter attributes are defined:

This attribute cannot be applied to return values.

``range(<ty> <a>, <b>)``
This attribute expresses the possible range the parameter is in. The value of
the parameter is in the specified range or is poison.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This attribute expresses the possible range the parameter is in. The value of
the parameter is in the specified range or is poison.
This attribute expresses the possible range of the parameter or return value.
If the value is not in the specified range, it is converted to poison.

The arguments passed to ``range`` has the following properties:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The arguments passed to ``range`` has the following properties:
The arguments passed to ``range`` have the following properties:


- The type must match the scalar type of the parameter.
- The pair ``a,b`` represents the range ``[a,b)``.
- Both ``a`` and ``b`` are constants.
- The range is allowed to wrap.
- The range should not represent the full or empty set. That is,
``a!=b``.
nikic marked this conversation as resolved.
Show resolved Hide resolved

This attribute may only be applied to parameters with integer or vector of
integer types.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This attribute may only be applied to parameters with integer or vector of
integer types.
This attribute may only be applied to parameters or return values with integer or vector of
integer types.


For vector-typed parameters, the range is applied element-wise.

.. _gc:

Garbage Collector Strategy Names
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/ADT/FoldingSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#ifndef LLVM_ADT_FOLDINGSET_H
#define LLVM_ADT_FOLDINGSET_H

#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -354,6 +355,12 @@ class FoldingSetNodeID {
AddInteger(unsigned(I));
AddInteger(unsigned(I >> 32));
}
void AddInteger(APInt Int) {
nikic marked this conversation as resolved.
Show resolved Hide resolved
const auto *Parts = Int.getRawData();
for (int i = 0, N = Int.getNumWords(); i < N; ++i) {
AddInteger(Parts[i]);
}
}

void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); }
void AddString(StringRef String);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ namespace llvm {
bool parseFnAttributeValuePairs(AttrBuilder &B,
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool parseRangeAttr(AttrBuilder &B);
bool parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
Attribute::AttrKind AttrKind);

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ enum AttributeKindCodes {
ATTR_KIND_WRITABLE = 89,
ATTR_KIND_CORO_ONLY_DESTROY_WHEN_COMPLETE = 90,
ATTR_KIND_DEAD_ON_UNWIND = 91,
ATTR_KIND_RANGE = 92,
};

enum ComdatSelectionKindCodes {
Expand Down
23 changes: 23 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class AttributeMask;
class AttributeImpl;
class AttributeListImpl;
class AttributeSetNode;
class ConstantRange;
class FoldingSetNodeID;
class Function;
class LLVMContext;
Expand Down Expand Up @@ -103,6 +104,9 @@ class Attribute {
static bool isTypeAttrKind(AttrKind Kind) {
return Kind >= FirstTypeAttr && Kind <= LastTypeAttr;
}
static bool isConstantRangeAttrKind(AttrKind Kind) {
return Kind >= FirstConstantRangeAttr && Kind <= LastConstantRangeAttr;
}

static bool canUseAsFnAttr(AttrKind Kind);
static bool canUseAsParamAttr(AttrKind Kind);
Expand All @@ -125,6 +129,8 @@ class Attribute {
static Attribute get(LLVMContext &Context, StringRef Kind,
StringRef Val = StringRef());
static Attribute get(LLVMContext &Context, AttrKind Kind, Type *Ty);
static Attribute get(LLVMContext &Context, AttrKind Kind,
const ConstantRange &CR);

/// Return a uniquified Attribute object that has the specific
/// alignment set.
Expand Down Expand Up @@ -180,6 +186,9 @@ class Attribute {
/// Return true if the attribute is a type attribute.
bool isTypeAttribute() const;

/// Return true if the attribute is a ConstantRange attribute.
bool isConstantRangeAttribute() const;

/// Return true if the attribute is any kind of attribute.
bool isValid() const { return pImpl; }

Expand Down Expand Up @@ -213,6 +222,10 @@ class Attribute {
/// a type attribute.
Type *getValueAsType() const;

/// Return the attribute's value as a ConstantRange. This requires the
/// attribute to be a ConstantRange attribute.
ConstantRange getValueAsConstantRange() const;

/// Returns the alignment field of an attribute as a byte alignment
/// value.
MaybeAlign getAlignment() const;
Expand Down Expand Up @@ -251,6 +264,9 @@ class Attribute {
/// Return the FPClassTest for nofpclass
FPClassTest getNoFPClass() const;

/// Returns the value of the range attribute.
ConstantRange getRange() const;

/// The Attribute is converted to a string of equivalent mnemonic. This
/// is, presumably, for writing out the mnemonics for the assembly writer.
std::string getAsString(bool InAttrGrp = false) const;
Expand Down Expand Up @@ -1189,6 +1205,13 @@ class AttrBuilder {
// Add nofpclass attribute
AttrBuilder &addNoFPClassAttr(FPClassTest NoFPClassMask);

/// Add a ConstantRange attribute with the given range.
AttrBuilder &addConstantRangeAttr(Attribute::AttrKind Kind,
const ConstantRange &CR);

/// Add range attribute.
AttrBuilder &addRangeAttr(const ConstantRange &CR);

ArrayRef<Attribute> attrs() const { return Attrs; }

bool operator==(const AttrBuilder &B) const;
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class StrBoolAttr<string S> : Attr<S, []>;
/// Arbitrary string attribute.
class ComplexStrAttr<string S, list<AttrProperty> P> : Attr<S, P>;

/// ConstantRange attribute.
class ConstantRangeAttr<string S, list<AttrProperty> P> : Attr<S, P>;

/// Target-independent enum attributes.

/// Alignment of parameter (5 bits) stored as log2 of alignment with +1 bias.
Expand Down Expand Up @@ -218,6 +221,9 @@ def OptimizeNone : EnumAttr<"optnone", [FnAttr]>;
/// Similar to byval but without a copy.
def Preallocated : TypeAttr<"preallocated", [FnAttr, ParamAttr]>;

/// Function does not access memory.
nikic marked this conversation as resolved.
Show resolved Hide resolved
def Range : ConstantRangeAttr<"range", [ParamAttr, RetAttr]>;

/// Function does not access memory.
def ReadNone : EnumAttr<"readnone", [ParamAttr]>;

Expand Down
48 changes: 48 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,9 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,

return true;
}
case Attribute::Range: {
return parseRangeAttr(B);
}
nikic marked this conversation as resolved.
Show resolved Hide resolved
default:
B.addAttribute(Attr);
Lex.Lex();
Expand Down Expand Up @@ -2997,6 +3000,51 @@ bool LLParser::parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
return false;
}

/// parseRangeAttr
/// ::= range(<ty> <n>,<n>)
bool LLParser::parseRangeAttr(AttrBuilder &B) {
Lex.Lex();

APInt Lower;
APInt Upper;
Type *Ty = nullptr;
LocTy TyLoc;

auto ParseAPSInt = [&](llvm::TypeSize BitWidth, APInt &Val) {
if (Lex.getKind() != lltok::APSInt)
return tokError("expected integer");
if (Lex.getAPSIntVal().getBitWidth() > BitWidth)
return tokError("integer is to large for the BitWidth");
nikic marked this conversation as resolved.
Show resolved Hide resolved
Val = Lex.getAPSIntVal().extend(BitWidth);
Lex.Lex();
return false;
};

if (!EatIfPresent(lltok::lparen))
nikic marked this conversation as resolved.
Show resolved Hide resolved
return tokError("expected '('");
if (parseType(Ty, TyLoc))
return true;
if (!Ty->isIntegerTy())
return error(TyLoc, "The range must have integer type!");

auto BitWidth = Ty->getPrimitiveSizeInBits();
nikic marked this conversation as resolved.
Show resolved Hide resolved

if (ParseAPSInt(BitWidth, Lower))
return true;
if (!EatIfPresent(lltok::comma))
nikic marked this conversation as resolved.
Show resolved Hide resolved
return tokError("expected ','");
if (ParseAPSInt(BitWidth, Upper))
return true;
if (Lower == Upper)
return tokError("The range should not represent the full or empty set!");
nikic marked this conversation as resolved.
Show resolved Hide resolved

if (!EatIfPresent(lltok::rparen))
nikic marked this conversation as resolved.
Show resolved Hide resolved
return tokError("expected ')'");

B.addRangeAttr(ConstantRange(Lower, Upper));
return false;
}

/// parseOptionalOperandBundles
/// ::= /*empty*/
/// ::= '[' OperandBundle [, OperandBundle ]* ']'
Expand Down
30 changes: 30 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2103,6 +2103,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::CoroDestroyOnlyWhenComplete;
case bitc::ATTR_KIND_DEAD_ON_UNWIND:
return Attribute::DeadOnUnwind;
case bitc::ATTR_KIND_RANGE:
return Attribute::Range;
}
}

Expand Down Expand Up @@ -2272,6 +2274,34 @@ Error BitcodeReader::parseAttributeGroupBlock() {
return error("Not a type attribute");

B.addTypeAttr(Kind, HasType ? getTypeByID(Record[++i]) : nullptr);
} else if (Record[i] == 7 || Record[i] == 8) {
bool WideAPInt = Record[i++] == 8;
Attribute::AttrKind Kind;
if (Error Err = parseAttrKind(Record[i++], &Kind))
return Err;
if (!Attribute::isConstantRangeAttrKind(Kind))
return error("Not a ConstantRange attribute");

unsigned ValueBitWidth = Record[i++];
unsigned ActiveWords = 1;
if (WideAPInt)
ActiveWords = Record[i++];
APInt Lower =
readWideAPInt(ArrayRef(&Record[i], ActiveWords), ValueBitWidth);
i += ActiveWords;
ActiveWords = 1;
if (WideAPInt)
ActiveWords = Record[i++];
APInt Upper =
readWideAPInt(ArrayRef(&Record[i], ActiveWords), ValueBitWidth);
i += ActiveWords - 1;

if (Lower == Upper)
return error(
"The range should not represent the full or empty set!");

ConstantRange Range(Lower, Upper);
B.addConstantRangeAttr(Kind, Range);
} else {
return error("Invalid attribute group entry");
}
Expand Down
59 changes: 39 additions & 20 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_CORO_ONLY_DESTROY_WHEN_COMPLETE;
case Attribute::DeadOnUnwind:
return bitc::ATTR_KIND_DEAD_ON_UNWIND;
case Attribute::Range:
return bitc::ATTR_KIND_RANGE;
case Attribute::EndAttrKinds:
llvm_unreachable("Can not encode end-attribute kinds marker.");
case Attribute::None:
Expand All @@ -856,6 +858,24 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
llvm_unreachable("Trying to encode unknown attribute");
}

static void emitSignedInt64(SmallVectorImpl<uint64_t> &Vals, uint64_t V) {
if ((int64_t)V >= 0)
Vals.push_back(V << 1);
else
Vals.push_back((-V << 1) | 1);
}

static void emitWideAPInt(SmallVectorImpl<uint64_t> &Vals, const APInt &A) {
// We have an arbitrary precision integer value to write whose
// bit width is > 64. However, in canonical unsigned integer
// format it is likely that the high bits are going to be zero.
// So, we only write the number of active words.
unsigned NumWords = A.getActiveWords();
const uint64_t *RawData = A.getRawData();
for (unsigned i = 0; i < NumWords; i++)
emitSignedInt64(Vals, RawData[i]);
}

void ModuleBitcodeWriter::writeAttributeGroupTable() {
const std::vector<ValueEnumerator::IndexAndAttrSet> &AttrGrps =
VE.getAttributeGroups();
Expand Down Expand Up @@ -889,13 +909,30 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
Record.append(Val.begin(), Val.end());
Record.push_back(0);
}
} else {
assert(Attr.isTypeAttribute());
} else if (Attr.isTypeAttribute()) {
Type *Ty = Attr.getValueAsType();
Record.push_back(Ty ? 6 : 5);
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
if (Ty)
Record.push_back(VE.getTypeID(Attr.getValueAsType()));
} else {
assert(Attr.isConstantRangeAttribute());
ConstantRange Range = Attr.getValueAsConstantRange();
bool WideAPInt = Range.getBitWidth() > 64;
Record.push_back(WideAPInt ? 8 : 7);
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
Record.push_back(Range.getBitWidth());
if (WideAPInt) {
const APInt &Lower = Range.getLower();
Record.push_back(Lower.getActiveWords());
emitWideAPInt(Record, Lower);
const APInt &Upper = Range.getUpper();
Record.push_back(Upper.getActiveWords());
emitWideAPInt(Record, Upper);
} else {
emitSignedInt64(Record, *Range.getLower().getRawData());
emitSignedInt64(Record, *Range.getUpper().getRawData());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to have a ConstantRange encoding that can be reused for other purposes as well. The way this is implemented now this will be a bit hard, as the wide distinction is part of the attribute kind.

I have some local changes that also needed this, and these are the implementations I used:

// BitcodeWriter
static void emitConstantRange(SmallVectorImpl<uint64_t> &Record,
                              const ConstantRange &CR) {
  unsigned BitWidth = CR.getBitWidth();
  Record.push_back(BitWidth);
  if (BitWidth > 64) {
    Record.push_back(CR.getLower().getActiveWords() |
        (uint64_t(CR.getUpper().getActiveWords()) << 32));
    emitWideAPInt(Record, CR.getLower());
    emitWideAPInt(Record, CR.getUpper());
  } else {
    emitSignedInt64(Record, CR.getLower().getSExtValue());
    emitSignedInt64(Record, CR.getUpper().getSExtValue());
  }
}

// BitcodeReader
Expected<ConstantRange> readConstantRange(ArrayRef<uint64_t> Record,
                                          unsigned &OpNum) {
  if (Record.size() - OpNum < 3)
    return error("Too few records for range");
  unsigned BitWidth = Record[OpNum++];
  if (BitWidth > 64) {
    unsigned LowerActiveWords = Record[OpNum];
    unsigned UpperActiveWords = Record[OpNum++] >> 32;
    if (Record.size() - OpNum < LowerActiveWords + UpperActiveWords)
      return error("Too few records for range");
    APInt Lower =
        readWideAPInt(ArrayRef(&Record[OpNum], LowerActiveWords), BitWidth);
    OpNum += LowerActiveWords;
    APInt Upper =
        readWideAPInt(ArrayRef(&Record[OpNum], UpperActiveWords), BitWidth);
    OpNum += UpperActiveWords;
    return ConstantRange(Lower, Upper);
  } else {        
    int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]);
    int64_t End = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]);
    return ConstantRange(APInt(BitWidth, Start), APInt(BitWidth, End));
  }
} 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes much nicer to have separat functions was trying to restrict number of used bytes as other types did it.

}
}

Expand Down Expand Up @@ -1716,24 +1753,6 @@ void ModuleBitcodeWriter::writeDIGenericSubrange(
Record.clear();
}

static void emitSignedInt64(SmallVectorImpl<uint64_t> &Vals, uint64_t V) {
if ((int64_t)V >= 0)
Vals.push_back(V << 1);
else
Vals.push_back((-V << 1) | 1);
}

static void emitWideAPInt(SmallVectorImpl<uint64_t> &Vals, const APInt &A) {
// We have an arbitrary precision integer value to write whose
// bit width is > 64. However, in canonical unsigned integer
// format it is likely that the high bits are going to be zero.
// So, we only write the number of active words.
unsigned NumWords = A.getActiveWords();
const uint64_t *RawData = A.getRawData();
for (unsigned i = 0; i < NumWords; i++)
emitSignedInt64(Vals, RawData[i]);
}

void ModuleBitcodeWriter::writeDIEnumerator(const DIEnumerator *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
Expand Down
Loading
Loading