Skip to content

Commit

Permalink
Add the 'initializes' attribute langref and support (llvm#84803)
Browse files Browse the repository at this point in the history
We propose adding a new LLVM attribute,
`initializes((Lo1,Hi1),(Lo2,Hi2),...)`, which expresses the notion of
memory space (i.e., intervals, in bytes) that the argument pointing to
is initialized in the function.

Will commit the attribute inferring in the follow-up PRs.


https://discourse.llvm.org/t/rfc-llvm-new-initialized-parameter-attribute-for-improved-interprocedural-dse/77337
  • Loading branch information
haopliu authored and AlexisPerry committed Jun 27, 2024
1 parent a9da344 commit 9932968
Show file tree
Hide file tree
Showing 23 changed files with 730 additions and 20 deletions.
21 changes: 21 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,27 @@ Currently, only the following parameter attributes are defined:
``readonly`` or a ``memory`` attribute that does not contain
``argmem: write``.

``initializes((Lo1, Hi1), ...)``
This attribute indicates that the function initializes the ranges of the
pointer parameter's memory, ``[%p+LoN, %p+HiN)``. Initialization of memory
means the first memory access is a non-volatile, non-atomic write. The
write must happen before the function returns. If the function unwinds,
the write may not happen.

This attribute only holds for the memory accessed via this pointer
parameter. Other arbitrary accesses to the same memory via other pointers
are allowed.

The ``writable`` or ``dereferenceable`` attribute do not imply the
``initializes`` attribute. The ``initializes`` attribute does not imply
``writeonly`` since ``initializes`` allows reading from the pointer
after writing.

This attribute is a list of constant ranges in ascending order with no
overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit integers,
and negative values are allowed in case the argument points partway into
an allocation. An empty list is not allowed.

``dead_on_unwind``
At a high level, this attribute indicates that the pointer argument is dead
if the call unwinds, in the sense that the caller will not depend on the
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 @@ -372,6 +372,7 @@ namespace llvm {
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool parseRangeAttr(AttrBuilder &B);
bool parseInitializesAttr(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 @@ -755,6 +755,7 @@ enum AttributeKindCodes {
ATTR_KIND_DEAD_ON_UNWIND = 91,
ATTR_KIND_RANGE = 92,
ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93,
ATTR_KIND_INITIALIZES = 94,
};

enum ComdatSelectionKindCodes {
Expand Down
24 changes: 24 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class AttributeImpl;
class AttributeListImpl;
class AttributeSetNode;
class ConstantRange;
class ConstantRangeList;
class FoldingSetNodeID;
class Function;
class LLVMContext;
Expand Down Expand Up @@ -107,6 +108,10 @@ class Attribute {
static bool isConstantRangeAttrKind(AttrKind Kind) {
return Kind >= FirstConstantRangeAttr && Kind <= LastConstantRangeAttr;
}
static bool isConstantRangeListAttrKind(AttrKind Kind) {
return Kind >= FirstConstantRangeListAttr &&
Kind <= LastConstantRangeListAttr;
}

static bool canUseAsFnAttr(AttrKind Kind);
static bool canUseAsParamAttr(AttrKind Kind);
Expand All @@ -131,6 +136,8 @@ class Attribute {
static Attribute get(LLVMContext &Context, AttrKind Kind, Type *Ty);
static Attribute get(LLVMContext &Context, AttrKind Kind,
const ConstantRange &CR);
static Attribute get(LLVMContext &Context, AttrKind Kind,
ArrayRef<ConstantRange> Val);

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

/// Return true if the attribute is a ConstantRangeList attribute.
bool isConstantRangeListAttribute() const;

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

Expand Down Expand Up @@ -226,6 +236,10 @@ class Attribute {
/// attribute to be a ConstantRange attribute.
const ConstantRange &getValueAsConstantRange() const;

/// Return the attribute's value as a ConstantRange array. This requires the
/// attribute to be a ConstantRangeList attribute.
ArrayRef<ConstantRange> getValueAsConstantRangeList() const;

/// Returns the alignment field of an attribute as a byte alignment
/// value.
MaybeAlign getAlignment() const;
Expand Down Expand Up @@ -267,6 +281,9 @@ class Attribute {
/// Returns the value of the range attribute.
const ConstantRange &getRange() const;

/// Returns the value of the initializes attribute.
ArrayRef<ConstantRange> getInitializes() 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 @@ -1222,6 +1239,13 @@ class AttrBuilder {
/// Add range attribute.
AttrBuilder &addRangeAttr(const ConstantRange &CR);

/// Add a ConstantRangeList attribute with the given ranges.
AttrBuilder &addConstantRangeListAttr(Attribute::AttrKind Kind,
ArrayRef<ConstantRange> Val);

/// Add initializes attribute.
AttrBuilder &addInitializesAttr(const ConstantRangeList &CRL);

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 @@ -47,6 +47,9 @@ class ComplexStrAttr<string S, list<AttrProperty> P> : Attr<S, P>;
/// ConstantRange attribute.
class ConstantRangeAttr<string S, list<AttrProperty> P> : Attr<S, P>;

/// ConstantRangeList attribute.
class ConstantRangeListAttr<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 @@ -112,6 +115,9 @@ def FnRetThunkExtern : EnumAttr<"fn_ret_thunk_extern", [FnAttr]>;
/// Pass structure in an alloca.
def InAlloca : TypeAttr<"inalloca", [ParamAttr]>;

/// Pointer argument memory is initialized.
def Initializes : ConstantRangeListAttr<"initializes", [ParamAttr]>;

/// Source said inlining was desirable.
def InlineHint : EnumAttr<"inlinehint", [FnAttr]>;

Expand Down
93 changes: 93 additions & 0 deletions llvm/include/llvm/IR/ConstantRangeList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//===- ConstantRangeList.h - A list of constant ranges ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Represent a list of signed ConstantRange and do NOT support wrap around the
// end of the numeric range. Ranges in the list are ordered and not overlapping.
// Ranges should have the same bitwidth. Each range's lower should be less than
// its upper.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_CONSTANTRANGELIST_H
#define LLVM_IR_CONSTANTRANGELIST_H

#include "llvm/ADT/APInt.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/Support/Debug.h"
#include <cstddef>
#include <cstdint>

namespace llvm {

class raw_ostream;

/// This class represents a list of constant ranges.
class [[nodiscard]] ConstantRangeList {
SmallVector<ConstantRange, 2> Ranges;

public:
ConstantRangeList() = default;
ConstantRangeList(ArrayRef<ConstantRange> RangesRef) {
assert(isOrderedRanges(RangesRef));
for (const ConstantRange &R : RangesRef) {
assert(R.getBitWidth() == getBitWidth());
Ranges.push_back(R);
}
}

// Return true if the ranges are non-overlapping and increasing.
static bool isOrderedRanges(ArrayRef<ConstantRange> RangesRef);
static std::optional<ConstantRangeList>
getConstantRangeList(ArrayRef<ConstantRange> RangesRef);

ArrayRef<ConstantRange> rangesRef() const { return Ranges; }
SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
SmallVectorImpl<ConstantRange>::const_iterator begin() const {
return Ranges.begin();
}
SmallVectorImpl<ConstantRange>::const_iterator end() const {
return Ranges.end();
}
ConstantRange getRange(unsigned i) const { return Ranges[i]; }

/// Return true if this list contains no members.
bool empty() const { return Ranges.empty(); }

/// Get the bit width of this ConstantRangeList.
uint32_t getBitWidth() const { return 64; }

/// Return the number of ranges in this ConstantRangeList.
size_t size() const { return Ranges.size(); }

/// Insert a new range to Ranges and keep the list ordered.
void insert(const ConstantRange &NewRange);
void insert(int64_t Lower, int64_t Upper) {
insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
APInt(64, Upper, /*isSigned=*/true)));
}

/// Return true if this range list is equal to another range list.
bool operator==(const ConstantRangeList &CRL) const {
return Ranges == CRL.Ranges;
}
bool operator!=(const ConstantRangeList &CRL) const {
return !operator==(CRL);
}

/// Print out the ranges to a stream.
void print(raw_ostream &OS) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() const;
#endif
};

} // end namespace llvm

#endif // LLVM_IR_CONSTANTRANGELIST_H
49 changes: 49 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Comdat.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/ConstantRangeList.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
Expand Down Expand Up @@ -1626,6 +1627,8 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
}
case Attribute::Range:
return parseRangeAttr(B);
case Attribute::Initializes:
return parseInitializesAttr(B);
default:
B.addAttribute(Attr);
Lex.Lex();
Expand Down Expand Up @@ -3101,6 +3104,52 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
return false;
}

/// parseInitializesAttr
/// ::= initializes((Lo1,Hi1),(Lo2,Hi2),...)
bool LLParser::parseInitializesAttr(AttrBuilder &B) {
Lex.Lex();

auto ParseAPSInt = [&](APInt &Val) {
if (Lex.getKind() != lltok::APSInt)
return tokError("expected integer");
Val = Lex.getAPSIntVal().extend(64);
Lex.Lex();
return false;
};

if (parseToken(lltok::lparen, "expected '('"))
return true;

SmallVector<ConstantRange, 2> RangeList;
// Parse each constant range.
do {
APInt Lower, Upper;
if (parseToken(lltok::lparen, "expected '('"))
return true;

if (ParseAPSInt(Lower) || parseToken(lltok::comma, "expected ','") ||
ParseAPSInt(Upper))
return true;

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

if (parseToken(lltok::rparen, "expected ')'"))
return true;

RangeList.push_back(ConstantRange(Lower, Upper));
} while (EatIfPresent(lltok::comma));

if (parseToken(lltok::rparen, "expected ')'"))
return true;

auto CRLOrNull = ConstantRangeList::getConstantRangeList(RangeList);
if (!CRLOrNull.has_value())
return tokError("Invalid (unordered or overlapping) range list");
B.addInitializesAttr(*CRLOrNull);
return false;
}

/// parseOptionalOperandBundles
/// ::= /*empty*/
/// ::= '[' OperandBundle [, OperandBundle ]* ']'
Expand Down
49 changes: 44 additions & 5 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Comdat.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/ConstantRangeList.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
Expand Down Expand Up @@ -838,10 +839,10 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
}

Expected<ConstantRange> readConstantRange(ArrayRef<uint64_t> Record,
unsigned &OpNum) {
if (Record.size() - OpNum < 3)
unsigned &OpNum,
unsigned BitWidth) {
if (Record.size() - OpNum < 2)
return error("Too few records for range");
unsigned BitWidth = Record[OpNum++];
if (BitWidth > 64) {
unsigned LowerActiveWords = Record[OpNum];
unsigned UpperActiveWords = Record[OpNum++] >> 32;
Expand All @@ -861,6 +862,14 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
}
}

Expected<ConstantRange>
readBitWidthAndConstantRange(ArrayRef<uint64_t> Record, unsigned &OpNum) {
if (Record.size() - OpNum < 1)
return error("Too few records for range");
unsigned BitWidth = Record[OpNum++];
return readConstantRange(Record, OpNum, BitWidth);
}

/// Upgrades old-style typeless byval/sret/inalloca attributes by adding the
/// corresponding argument's pointee type. Also upgrades intrinsics that now
/// require an elementtype attribute.
Expand Down Expand Up @@ -2174,6 +2183,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::DeadOnUnwind;
case bitc::ATTR_KIND_RANGE:
return Attribute::Range;
case bitc::ATTR_KIND_INITIALIZES:
return Attribute::Initializes;
}
}

Expand Down Expand Up @@ -2352,12 +2363,39 @@ Error BitcodeReader::parseAttributeGroupBlock() {
if (!Attribute::isConstantRangeAttrKind(Kind))
return error("Not a ConstantRange attribute");

Expected<ConstantRange> MaybeCR = readConstantRange(Record, i);
Expected<ConstantRange> MaybeCR =
readBitWidthAndConstantRange(Record, i);
if (!MaybeCR)
return MaybeCR.takeError();
i--;

B.addConstantRangeAttr(Kind, MaybeCR.get());
} else if (Record[i] == 8) {
Attribute::AttrKind Kind;

i++;
if (Error Err = parseAttrKind(Record[i++], &Kind))
return Err;
if (!Attribute::isConstantRangeListAttrKind(Kind))
return error("Not a constant range list attribute");

SmallVector<ConstantRange, 2> Val;
if (i + 2 > e)
return error("Too few records for constant range list");
unsigned RangeSize = Record[i++];
unsigned BitWidth = Record[i++];
for (unsigned Idx = 0; Idx < RangeSize; ++Idx) {
Expected<ConstantRange> MaybeCR =
readConstantRange(Record, i, BitWidth);
if (!MaybeCR)
return MaybeCR.takeError();
Val.push_back(MaybeCR.get());
}
i--;

if (!ConstantRangeList::isOrderedRanges(Val))
return error("Invalid (unordered or overlapping) range list");
B.addConstantRangeListAttr(Kind, Val);
} else {
return error("Invalid attribute group entry");
}
Expand Down Expand Up @@ -3372,7 +3410,8 @@ Error BitcodeReader::parseConstants() {
(void)InRangeIndex;
} else if (BitCode == bitc::CST_CODE_CE_GEP_WITH_INRANGE) {
Flags = Record[OpNum++];
Expected<ConstantRange> MaybeInRange = readConstantRange(Record, OpNum);
Expected<ConstantRange> MaybeInRange =
readBitWidthAndConstantRange(Record, OpNum);
if (!MaybeInRange)
return MaybeInRange.takeError();
InRange = MaybeInRange.get();
Expand Down
Loading

0 comments on commit 9932968

Please sign in to comment.