Skip to content

Commit 5ece35d

Browse files
authored
Add the 'initializes' attribute langref and support (#84803)
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
1 parent f1f3c34 commit 5ece35d

23 files changed

+730
-20
lines changed

llvm/docs/LangRef.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,27 @@ Currently, only the following parameter attributes are defined:
16311631
``readonly`` or a ``memory`` attribute that does not contain
16321632
``argmem: write``.
16331633

1634+
``initializes((Lo1, Hi1), ...)``
1635+
This attribute indicates that the function initializes the ranges of the
1636+
pointer parameter's memory, ``[%p+LoN, %p+HiN)``. Initialization of memory
1637+
means the first memory access is a non-volatile, non-atomic write. The
1638+
write must happen before the function returns. If the function unwinds,
1639+
the write may not happen.
1640+
1641+
This attribute only holds for the memory accessed via this pointer
1642+
parameter. Other arbitrary accesses to the same memory via other pointers
1643+
are allowed.
1644+
1645+
The ``writable`` or ``dereferenceable`` attribute do not imply the
1646+
``initializes`` attribute. The ``initializes`` attribute does not imply
1647+
``writeonly`` since ``initializes`` allows reading from the pointer
1648+
after writing.
1649+
1650+
This attribute is a list of constant ranges in ascending order with no
1651+
overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit integers,
1652+
and negative values are allowed in case the argument points partway into
1653+
an allocation. An empty list is not allowed.
1654+
16341655
``dead_on_unwind``
16351656
At a high level, this attribute indicates that the pointer argument is dead
16361657
if the call unwinds, in the sense that the caller will not depend on the

llvm/include/llvm/AsmParser/LLParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ namespace llvm {
372372
std::vector<unsigned> &FwdRefAttrGrps,
373373
bool inAttrGrp, LocTy &BuiltinLoc);
374374
bool parseRangeAttr(AttrBuilder &B);
375+
bool parseInitializesAttr(AttrBuilder &B);
375376
bool parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
376377
Attribute::AttrKind AttrKind);
377378

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ enum AttributeKindCodes {
755755
ATTR_KIND_DEAD_ON_UNWIND = 91,
756756
ATTR_KIND_RANGE = 92,
757757
ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93,
758+
ATTR_KIND_INITIALIZES = 94,
758759
};
759760

760761
enum ComdatSelectionKindCodes {

llvm/include/llvm/IR/Attributes.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class AttributeImpl;
3838
class AttributeListImpl;
3939
class AttributeSetNode;
4040
class ConstantRange;
41+
class ConstantRangeList;
4142
class FoldingSetNodeID;
4243
class Function;
4344
class LLVMContext;
@@ -107,6 +108,10 @@ class Attribute {
107108
static bool isConstantRangeAttrKind(AttrKind Kind) {
108109
return Kind >= FirstConstantRangeAttr && Kind <= LastConstantRangeAttr;
109110
}
111+
static bool isConstantRangeListAttrKind(AttrKind Kind) {
112+
return Kind >= FirstConstantRangeListAttr &&
113+
Kind <= LastConstantRangeListAttr;
114+
}
110115

111116
static bool canUseAsFnAttr(AttrKind Kind);
112117
static bool canUseAsParamAttr(AttrKind Kind);
@@ -131,6 +136,8 @@ class Attribute {
131136
static Attribute get(LLVMContext &Context, AttrKind Kind, Type *Ty);
132137
static Attribute get(LLVMContext &Context, AttrKind Kind,
133138
const ConstantRange &CR);
139+
static Attribute get(LLVMContext &Context, AttrKind Kind,
140+
ArrayRef<ConstantRange> Val);
134141

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

199+
/// Return true if the attribute is a ConstantRangeList attribute.
200+
bool isConstantRangeListAttribute() const;
201+
192202
/// Return true if the attribute is any kind of attribute.
193203
bool isValid() const { return pImpl; }
194204

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

239+
/// Return the attribute's value as a ConstantRange array. This requires the
240+
/// attribute to be a ConstantRangeList attribute.
241+
ArrayRef<ConstantRange> getValueAsConstantRangeList() const;
242+
229243
/// Returns the alignment field of an attribute as a byte alignment
230244
/// value.
231245
MaybeAlign getAlignment() const;
@@ -267,6 +281,9 @@ class Attribute {
267281
/// Returns the value of the range attribute.
268282
const ConstantRange &getRange() const;
269283

284+
/// Returns the value of the initializes attribute.
285+
ArrayRef<ConstantRange> getInitializes() const;
286+
270287
/// The Attribute is converted to a string of equivalent mnemonic. This
271288
/// is, presumably, for writing out the mnemonics for the assembly writer.
272289
std::string getAsString(bool InAttrGrp = false) const;
@@ -1222,6 +1239,13 @@ class AttrBuilder {
12221239
/// Add range attribute.
12231240
AttrBuilder &addRangeAttr(const ConstantRange &CR);
12241241

1242+
/// Add a ConstantRangeList attribute with the given ranges.
1243+
AttrBuilder &addConstantRangeListAttr(Attribute::AttrKind Kind,
1244+
ArrayRef<ConstantRange> Val);
1245+
1246+
/// Add initializes attribute.
1247+
AttrBuilder &addInitializesAttr(const ConstantRangeList &CRL);
1248+
12251249
ArrayRef<Attribute> attrs() const { return Attrs; }
12261250

12271251
bool operator==(const AttrBuilder &B) const;

llvm/include/llvm/IR/Attributes.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class ComplexStrAttr<string S, list<AttrProperty> P> : Attr<S, P>;
4747
/// ConstantRange attribute.
4848
class ConstantRangeAttr<string S, list<AttrProperty> P> : Attr<S, P>;
4949

50+
/// ConstantRangeList attribute.
51+
class ConstantRangeListAttr<string S, list<AttrProperty> P> : Attr<S, P>;
52+
5053
/// Target-independent enum attributes.
5154

5255
/// Alignment of parameter (5 bits) stored as log2 of alignment with +1 bias.
@@ -112,6 +115,9 @@ def FnRetThunkExtern : EnumAttr<"fn_ret_thunk_extern", [FnAttr]>;
112115
/// Pass structure in an alloca.
113116
def InAlloca : TypeAttr<"inalloca", [ParamAttr]>;
114117

118+
/// Pointer argument memory is initialized.
119+
def Initializes : ConstantRangeListAttr<"initializes", [ParamAttr]>;
120+
115121
/// Source said inlining was desirable.
116122
def InlineHint : EnumAttr<"inlinehint", [FnAttr]>;
117123

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===- ConstantRangeList.h - A list of constant ranges ----------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Represent a list of signed ConstantRange and do NOT support wrap around the
10+
// end of the numeric range. Ranges in the list are ordered and not overlapping.
11+
// Ranges should have the same bitwidth. Each range's lower should be less than
12+
// its upper.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_IR_CONSTANTRANGELIST_H
17+
#define LLVM_IR_CONSTANTRANGELIST_H
18+
19+
#include "llvm/ADT/APInt.h"
20+
#include "llvm/IR/ConstantRange.h"
21+
#include "llvm/Support/Debug.h"
22+
#include <cstddef>
23+
#include <cstdint>
24+
25+
namespace llvm {
26+
27+
class raw_ostream;
28+
29+
/// This class represents a list of constant ranges.
30+
class [[nodiscard]] ConstantRangeList {
31+
SmallVector<ConstantRange, 2> Ranges;
32+
33+
public:
34+
ConstantRangeList() = default;
35+
ConstantRangeList(ArrayRef<ConstantRange> RangesRef) {
36+
assert(isOrderedRanges(RangesRef));
37+
for (const ConstantRange &R : RangesRef) {
38+
assert(R.getBitWidth() == getBitWidth());
39+
Ranges.push_back(R);
40+
}
41+
}
42+
43+
// Return true if the ranges are non-overlapping and increasing.
44+
static bool isOrderedRanges(ArrayRef<ConstantRange> RangesRef);
45+
static std::optional<ConstantRangeList>
46+
getConstantRangeList(ArrayRef<ConstantRange> RangesRef);
47+
48+
ArrayRef<ConstantRange> rangesRef() const { return Ranges; }
49+
SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
50+
SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
51+
SmallVectorImpl<ConstantRange>::const_iterator begin() const {
52+
return Ranges.begin();
53+
}
54+
SmallVectorImpl<ConstantRange>::const_iterator end() const {
55+
return Ranges.end();
56+
}
57+
ConstantRange getRange(unsigned i) const { return Ranges[i]; }
58+
59+
/// Return true if this list contains no members.
60+
bool empty() const { return Ranges.empty(); }
61+
62+
/// Get the bit width of this ConstantRangeList.
63+
uint32_t getBitWidth() const { return 64; }
64+
65+
/// Return the number of ranges in this ConstantRangeList.
66+
size_t size() const { return Ranges.size(); }
67+
68+
/// Insert a new range to Ranges and keep the list ordered.
69+
void insert(const ConstantRange &NewRange);
70+
void insert(int64_t Lower, int64_t Upper) {
71+
insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
72+
APInt(64, Upper, /*isSigned=*/true)));
73+
}
74+
75+
/// Return true if this range list is equal to another range list.
76+
bool operator==(const ConstantRangeList &CRL) const {
77+
return Ranges == CRL.Ranges;
78+
}
79+
bool operator!=(const ConstantRangeList &CRL) const {
80+
return !operator==(CRL);
81+
}
82+
83+
/// Print out the ranges to a stream.
84+
void print(raw_ostream &OS) const;
85+
86+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
87+
void dump() const;
88+
#endif
89+
};
90+
91+
} // end namespace llvm
92+
93+
#endif // LLVM_IR_CONSTANTRANGELIST_H

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/IR/CallingConv.h"
2626
#include "llvm/IR/Comdat.h"
2727
#include "llvm/IR/ConstantRange.h"
28+
#include "llvm/IR/ConstantRangeList.h"
2829
#include "llvm/IR/Constants.h"
2930
#include "llvm/IR/DebugInfoMetadata.h"
3031
#include "llvm/IR/DerivedTypes.h"
@@ -1626,6 +1627,8 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
16261627
}
16271628
case Attribute::Range:
16281629
return parseRangeAttr(B);
1630+
case Attribute::Initializes:
1631+
return parseInitializesAttr(B);
16291632
default:
16301633
B.addAttribute(Attr);
16311634
Lex.Lex();
@@ -3101,6 +3104,52 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
31013104
return false;
31023105
}
31033106

3107+
/// parseInitializesAttr
3108+
/// ::= initializes((Lo1,Hi1),(Lo2,Hi2),...)
3109+
bool LLParser::parseInitializesAttr(AttrBuilder &B) {
3110+
Lex.Lex();
3111+
3112+
auto ParseAPSInt = [&](APInt &Val) {
3113+
if (Lex.getKind() != lltok::APSInt)
3114+
return tokError("expected integer");
3115+
Val = Lex.getAPSIntVal().extend(64);
3116+
Lex.Lex();
3117+
return false;
3118+
};
3119+
3120+
if (parseToken(lltok::lparen, "expected '('"))
3121+
return true;
3122+
3123+
SmallVector<ConstantRange, 2> RangeList;
3124+
// Parse each constant range.
3125+
do {
3126+
APInt Lower, Upper;
3127+
if (parseToken(lltok::lparen, "expected '('"))
3128+
return true;
3129+
3130+
if (ParseAPSInt(Lower) || parseToken(lltok::comma, "expected ','") ||
3131+
ParseAPSInt(Upper))
3132+
return true;
3133+
3134+
if (Lower == Upper)
3135+
return tokError("the range should not represent the full or empty set!");
3136+
3137+
if (parseToken(lltok::rparen, "expected ')'"))
3138+
return true;
3139+
3140+
RangeList.push_back(ConstantRange(Lower, Upper));
3141+
} while (EatIfPresent(lltok::comma));
3142+
3143+
if (parseToken(lltok::rparen, "expected ')'"))
3144+
return true;
3145+
3146+
auto CRLOrNull = ConstantRangeList::getConstantRangeList(RangeList);
3147+
if (!CRLOrNull.has_value())
3148+
return tokError("Invalid (unordered or overlapping) range list");
3149+
B.addInitializesAttr(*CRLOrNull);
3150+
return false;
3151+
}
3152+
31043153
/// parseOptionalOperandBundles
31053154
/// ::= /*empty*/
31063155
/// ::= '[' OperandBundle [, OperandBundle ]* ']'

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llvm/IR/CallingConv.h"
3131
#include "llvm/IR/Comdat.h"
3232
#include "llvm/IR/Constant.h"
33+
#include "llvm/IR/ConstantRangeList.h"
3334
#include "llvm/IR/Constants.h"
3435
#include "llvm/IR/DataLayout.h"
3536
#include "llvm/IR/DebugInfo.h"
@@ -838,10 +839,10 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
838839
}
839840

840841
Expected<ConstantRange> readConstantRange(ArrayRef<uint64_t> Record,
841-
unsigned &OpNum) {
842-
if (Record.size() - OpNum < 3)
842+
unsigned &OpNum,
843+
unsigned BitWidth) {
844+
if (Record.size() - OpNum < 2)
843845
return error("Too few records for range");
844-
unsigned BitWidth = Record[OpNum++];
845846
if (BitWidth > 64) {
846847
unsigned LowerActiveWords = Record[OpNum];
847848
unsigned UpperActiveWords = Record[OpNum++] >> 32;
@@ -861,6 +862,14 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
861862
}
862863
}
863864

865+
Expected<ConstantRange>
866+
readBitWidthAndConstantRange(ArrayRef<uint64_t> Record, unsigned &OpNum) {
867+
if (Record.size() - OpNum < 1)
868+
return error("Too few records for range");
869+
unsigned BitWidth = Record[OpNum++];
870+
return readConstantRange(Record, OpNum, BitWidth);
871+
}
872+
864873
/// Upgrades old-style typeless byval/sret/inalloca attributes by adding the
865874
/// corresponding argument's pointee type. Also upgrades intrinsics that now
866875
/// require an elementtype attribute.
@@ -2174,6 +2183,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
21742183
return Attribute::DeadOnUnwind;
21752184
case bitc::ATTR_KIND_RANGE:
21762185
return Attribute::Range;
2186+
case bitc::ATTR_KIND_INITIALIZES:
2187+
return Attribute::Initializes;
21772188
}
21782189
}
21792190

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

2355-
Expected<ConstantRange> MaybeCR = readConstantRange(Record, i);
2366+
Expected<ConstantRange> MaybeCR =
2367+
readBitWidthAndConstantRange(Record, i);
23562368
if (!MaybeCR)
23572369
return MaybeCR.takeError();
23582370
i--;
23592371

23602372
B.addConstantRangeAttr(Kind, MaybeCR.get());
2373+
} else if (Record[i] == 8) {
2374+
Attribute::AttrKind Kind;
2375+
2376+
i++;
2377+
if (Error Err = parseAttrKind(Record[i++], &Kind))
2378+
return Err;
2379+
if (!Attribute::isConstantRangeListAttrKind(Kind))
2380+
return error("Not a constant range list attribute");
2381+
2382+
SmallVector<ConstantRange, 2> Val;
2383+
if (i + 2 > e)
2384+
return error("Too few records for constant range list");
2385+
unsigned RangeSize = Record[i++];
2386+
unsigned BitWidth = Record[i++];
2387+
for (unsigned Idx = 0; Idx < RangeSize; ++Idx) {
2388+
Expected<ConstantRange> MaybeCR =
2389+
readConstantRange(Record, i, BitWidth);
2390+
if (!MaybeCR)
2391+
return MaybeCR.takeError();
2392+
Val.push_back(MaybeCR.get());
2393+
}
2394+
i--;
2395+
2396+
if (!ConstantRangeList::isOrderedRanges(Val))
2397+
return error("Invalid (unordered or overlapping) range list");
2398+
B.addConstantRangeListAttr(Kind, Val);
23612399
} else {
23622400
return error("Invalid attribute group entry");
23632401
}
@@ -3372,7 +3410,8 @@ Error BitcodeReader::parseConstants() {
33723410
(void)InRangeIndex;
33733411
} else if (BitCode == bitc::CST_CODE_CE_GEP_WITH_INRANGE) {
33743412
Flags = Record[OpNum++];
3375-
Expected<ConstantRange> MaybeInRange = readConstantRange(Record, OpNum);
3413+
Expected<ConstantRange> MaybeInRange =
3414+
readBitWidthAndConstantRange(Record, OpNum);
33763415
if (!MaybeInRange)
33773416
return MaybeInRange.takeError();
33783417
InRange = MaybeInRange.get();

0 commit comments

Comments
 (0)