Skip to content

Commit

Permalink
[IR] Add new Range attribute using new ConstantRange Attribute type
Browse files Browse the repository at this point in the history
  • Loading branch information
andjo403 committed Feb 27, 2024
1 parent 12df1cf commit 719f95f
Show file tree
Hide file tree
Showing 17 changed files with 321 additions and 26 deletions.
18 changes: 18 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,24 @@ 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. If the
value of the parameter is not in the specified range, a poison value is
returned instead.
The argument passed to ``range`` has 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``.

This attribute may only be applied to parameters 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) {
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.
def Range : ConstantRangeAttr<"range", [ParamAttr, RetAttr]>;

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

Expand Down
50 changes: 50 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);
}
default:
B.addAttribute(Attr);
Lex.Lex();
Expand Down Expand Up @@ -2997,6 +3000,53 @@ 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");
Val = Lex.getAPSIntVal().extend(BitWidth);
Lex.Lex();
return false;
};

if (!EatIfPresent(lltok::lparen))
return tokError("expected '('");
if (parseType(Ty, TyLoc))
return true;
if (!Ty->isIntegerTy())
return error(TyLoc, "must have integer type");

auto BitWidth = Ty->getPrimitiveSizeInBits();

if (!EatIfPresent(lltok::comma))
return tokError("expected ','");
if (ParseAPSInt(BitWidth, Lower))
return true;
if (!EatIfPresent(lltok::comma))
return tokError("expected ','");
if (ParseAPSInt(BitWidth, Upper))
return true;
if (Lower == Upper)
return tokError("The range should not represent the full or empty set!");

if (!EatIfPresent(lltok::rparen))
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());
}
}
}

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

0 comments on commit 719f95f

Please sign in to comment.