Skip to content

Commit 8cdecd4

Browse files
authored
[IR] Add getelementptr nusw and nuw flags (#90824)
This implements the `nusw` and `nuw` flags for `getelementptr` as proposed at https://discourse.llvm.org/t/rfc-add-nusw-and-nuw-flags-for-getelementptr/78672. The three possible flags are encapsulated in the new `GEPNoWrapFlags` class. Currently this class has a ctor from bool, interpreted as the InBounds flag. This ctor should be removed in the future, as code gets migrated to handle all flags. There are a few places annotated with `TODO(gep_nowrap)`, where I've had to touch code but opted to not infer or precisely preserve the new flags, so as to keep this as NFC as possible and make sure any changes of that kind get test coverage when they are made.
1 parent 49b760f commit 8cdecd4

File tree

28 files changed

+523
-102
lines changed

28 files changed

+523
-102
lines changed

llvm/docs/LangRef.rst

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11184,6 +11184,8 @@ Syntax:
1118411184

1118511185
<result> = getelementptr <ty>, ptr <ptrval>{, <ty> <idx>}*
1118611186
<result> = getelementptr inbounds <ty>, ptr <ptrval>{, <ty> <idx>}*
11187+
<result> = getelementptr nusw <ty>, ptr <ptrval>{, <ty> <idx>}*
11188+
<result> = getelementptr nuw <ty>, ptr <ptrval>{, <ty> <idx>}*
1118711189
<result> = getelementptr inrange(S,E) <ty>, ptr <ptrval>{, <ty> <idx>}*
1118811190
<result> = getelementptr <ty>, <N x ptr> <ptrval>, <vector index type> <idx>
1118911191

@@ -11299,27 +11301,47 @@ memory though, even if it happens to point into allocated storage. See the
1129911301
:ref:`Pointer Aliasing Rules <pointeraliasing>` section for more
1130011302
information.
1130111303

11302-
If the ``inbounds`` keyword is present, the result value of a
11303-
``getelementptr`` with any non-zero indices is a
11304-
:ref:`poison value <poisonvalues>` if one of the following rules is violated:
11305-
11306-
* The base pointer has an *in bounds* address of an allocated object, which
11304+
The ``getelementptr`` instruction may have a number of attributes that impose
11305+
additional rules. If any of the rules are violated, the result value is a
11306+
:ref:`poison value <poisonvalues>`. In cases where the base is a vector of
11307+
pointers, the attributes apply to each computation element-wise.
11308+
11309+
For ``nusw`` (no unsigned signed wrap):
11310+
11311+
* If the type of an index is larger than the pointer index type, the
11312+
truncation to the pointer index type preserves the signed value
11313+
(``trunc nsw``).
11314+
* The multiplication of an index by the type size does not wrap the pointer
11315+
index type in a signed sense (``mul nsw``).
11316+
* The successive addition of each offset (without adding the base address)
11317+
does not wrap the pointer index type in a signed sense (``add nsw``).
11318+
* The successive addition of the current address, truncated to the pointer
11319+
index type and interpreted as an unsigned number, and each offset,
11320+
interpreted as a signed number, does not wrap the pointer index type.
11321+
11322+
For ``nuw`` (no unsigned wrap):
11323+
11324+
* If the type of an index is larger than the pointer index type, the
11325+
truncation to the pointer index type preserves the unsigned value
11326+
(``trunc nuw``).
11327+
* The multiplication of an index by the type size does not wrap the pointer
11328+
index type in an unsigned sense (``mul nuw``).
11329+
* The successive addition of each offset (without adding the base address)
11330+
does not wrap the pointer index type in an unsigned sense (``add nuw``).
11331+
* The successive addition of the current address, truncated to the pointer
11332+
index type and interpreted as an unsigned number, and each offset, also
11333+
interpreted as an unsigned number, does not wrap the pointer index type
11334+
(``add nuw``).
11335+
11336+
For ``inbounds`` all rules of the ``nusw`` attribute apply. Additionally,
11337+
if the ``getelementptr`` has any non-zero indices, the following rules apply:
11338+
11339+
* The base pointer has an *in bounds* address of an allocated object, which
1130711340
means that it points into an allocated object, or to its end. Note that the
1130811341
object does not have to be live anymore; being in-bounds of a deallocated
1130911342
object is sufficient.
11310-
* If the type of an index is larger than the pointer index type, the
11311-
truncation to the pointer index type preserves the signed value.
11312-
* The multiplication of an index by the type size does not wrap the pointer
11313-
index type in a signed sense (``nsw``).
11314-
* The successive addition of each offset (without adding the base address) does
11315-
not wrap the pointer index type in a signed sense (``nsw``).
11316-
* The successive addition of the current address, interpreted as an unsigned
11317-
number, and each offset, interpreted as a signed number, does not wrap the
11318-
unsigned address space and remains *in bounds* of the allocated object.
11319-
As a corollary, if the added offset is non-negative, the addition does not
11320-
wrap in an unsigned sense (``nuw``).
11321-
* In cases where the base is a vector of pointers, the ``inbounds`` keyword
11322-
applies to each of the computations element-wise.
11343+
* During the successive addition of offsets to the address, the resulting
11344+
pointer must remain *in bounds* of the allocated object at each step.
1132311345

1132411346
Note that ``getelementptr`` with all-zero indices is always considered to be
1132511347
``inbounds``, even if the base pointer does not point to an allocated object.
@@ -11330,6 +11352,10 @@ These rules are based on the assumption that no allocated object may cross
1133011352
the unsigned address space boundary, and no allocated object may be larger
1133111353
than half the pointer index type space.
1133211354

11355+
If ``inbounds`` is present on a ``getelementptr`` instruction, the ``nusw``
11356+
attribute will be automatically set as well. For this reason, the ``nusw``
11357+
will also not be printed in textual IR if ``inbounds`` is already present.
11358+
1133311359
If the ``inrange(Start, End)`` attribute is present, loading from or
1133411360
storing to any pointer derived from the ``getelementptr`` has undefined
1133511361
behavior if the load or store would access memory outside the half-open range

llvm/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Changes to the LLVM IR
5151
----------------------
5252

5353
* Added Memory Model Relaxation Annotations (MMRAs).
54+
* Added ``nusw`` and ``nuw`` flags to ``getelementptr`` instruction.
5455
* Renamed ``llvm.experimental.vector.reverse`` intrinsic to ``llvm.vector.reverse``.
5556
* Renamed ``llvm.experimental.vector.splice`` intrinsic to ``llvm.vector.splice``.
5657
* Renamed ``llvm.experimental.vector.interleave2`` intrinsic to ``llvm.vector.interleave2``.

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ enum Kind {
109109
kw_fast,
110110
kw_nuw,
111111
kw_nsw,
112+
kw_nusw,
112113
kw_exact,
113114
kw_disjoint,
114115
kw_inbounds,

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ enum ConstantsCodes {
385385
CST_CODE_CSTRING = 9, // CSTRING: [values]
386386
CST_CODE_CE_BINOP = 10, // CE_BINOP: [opcode, opval, opval]
387387
CST_CODE_CE_CAST = 11, // CE_CAST: [opcode, opty, opval]
388-
CST_CODE_CE_GEP = 12, // CE_GEP: [n x operands]
388+
CST_CODE_CE_GEP_OLD = 12, // CE_GEP: [n x operands]
389389
CST_CODE_CE_SELECT = 13, // CE_SELECT: [opval, opval, opval]
390390
CST_CODE_CE_EXTRACTELT = 14, // CE_EXTRACTELT: [opty, opval, opval]
391391
CST_CODE_CE_INSERTELT = 15, // CE_INSERTELT: [opval, opval, opval]
@@ -412,6 +412,7 @@ enum ConstantsCodes {
412412
// asmdialect|unwind,
413413
// asmstr,conststr]
414414
CST_CODE_CE_GEP_WITH_INRANGE = 31, // [opty, flags, range, n x operands]
415+
CST_CODE_CE_GEP = 32, // [opty, flags, n x operands]
415416
};
416417

417418
/// CastOpcodes - These are values used in the bitcode files to encode which
@@ -524,6 +525,14 @@ enum PossiblyExactOperatorOptionalFlags { PEO_EXACT = 0 };
524525
/// PossiblyDisjointInst's SubclassOptionalData contents.
525526
enum PossiblyDisjointInstOptionalFlags { PDI_DISJOINT = 0 };
526527

528+
/// GetElementPtrOptionalFlags - Flags for serializing
529+
/// GEPOperator's SubclassOptionalData contents.
530+
enum GetElementPtrOptionalFlags {
531+
GEP_INBOUNDS = 0,
532+
GEP_NUSW = 1,
533+
GEP_NUW = 2,
534+
};
535+
527536
/// Encoded AtomicOrdering values.
528537
enum AtomicOrderingCodes {
529538
ORDERING_NOTATOMIC = 0,

llvm/include/llvm/IR/Constants.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/IR/Constant.h"
2929
#include "llvm/IR/ConstantRange.h"
3030
#include "llvm/IR/DerivedTypes.h"
31+
#include "llvm/IR/GEPNoWrapFlags.h"
3132
#include "llvm/IR/Intrinsics.h"
3233
#include "llvm/IR/OperandTraits.h"
3334
#include "llvm/IR/User.h"
@@ -1198,45 +1199,46 @@ class ConstantExpr : public Constant {
11981199
/// \param OnlyIfReducedTy see \a getWithOperands() docs.
11991200
static Constant *
12001201
getGetElementPtr(Type *Ty, Constant *C, ArrayRef<Constant *> IdxList,
1201-
bool InBounds = false,
1202+
GEPNoWrapFlags NW = GEPNoWrapFlags::none(),
12021203
std::optional<ConstantRange> InRange = std::nullopt,
12031204
Type *OnlyIfReducedTy = nullptr) {
12041205
return getGetElementPtr(
1205-
Ty, C, ArrayRef((Value *const *)IdxList.data(), IdxList.size()),
1206-
InBounds, InRange, OnlyIfReducedTy);
1206+
Ty, C, ArrayRef((Value *const *)IdxList.data(), IdxList.size()), NW,
1207+
InRange, OnlyIfReducedTy);
12071208
}
12081209
static Constant *
1209-
getGetElementPtr(Type *Ty, Constant *C, Constant *Idx, bool InBounds = false,
1210+
getGetElementPtr(Type *Ty, Constant *C, Constant *Idx,
1211+
GEPNoWrapFlags NW = GEPNoWrapFlags::none(),
12101212
std::optional<ConstantRange> InRange = std::nullopt,
12111213
Type *OnlyIfReducedTy = nullptr) {
12121214
// This form of the function only exists to avoid ambiguous overload
12131215
// warnings about whether to convert Idx to ArrayRef<Constant *> or
12141216
// ArrayRef<Value *>.
1215-
return getGetElementPtr(Ty, C, cast<Value>(Idx), InBounds, InRange,
1217+
return getGetElementPtr(Ty, C, cast<Value>(Idx), NW, InRange,
12161218
OnlyIfReducedTy);
12171219
}
12181220
static Constant *
12191221
getGetElementPtr(Type *Ty, Constant *C, ArrayRef<Value *> IdxList,
1220-
bool InBounds = false,
1222+
GEPNoWrapFlags NW = GEPNoWrapFlags::none(),
12211223
std::optional<ConstantRange> InRange = std::nullopt,
12221224
Type *OnlyIfReducedTy = nullptr);
12231225

12241226
/// Create an "inbounds" getelementptr. See the documentation for the
12251227
/// "inbounds" flag in LangRef.html for details.
12261228
static Constant *getInBoundsGetElementPtr(Type *Ty, Constant *C,
12271229
ArrayRef<Constant *> IdxList) {
1228-
return getGetElementPtr(Ty, C, IdxList, true);
1230+
return getGetElementPtr(Ty, C, IdxList, GEPNoWrapFlags::inBounds());
12291231
}
12301232
static Constant *getInBoundsGetElementPtr(Type *Ty, Constant *C,
12311233
Constant *Idx) {
12321234
// This form of the function only exists to avoid ambiguous overload
12331235
// warnings about whether to convert Idx to ArrayRef<Constant *> or
12341236
// ArrayRef<Value *>.
1235-
return getGetElementPtr(Ty, C, Idx, true);
1237+
return getGetElementPtr(Ty, C, Idx, GEPNoWrapFlags::inBounds());
12361238
}
12371239
static Constant *getInBoundsGetElementPtr(Type *Ty, Constant *C,
12381240
ArrayRef<Value *> IdxList) {
1239-
return getGetElementPtr(Ty, C, IdxList, true);
1241+
return getGetElementPtr(Ty, C, IdxList, GEPNoWrapFlags::inBounds());
12401242
}
12411243

12421244
static Constant *getExtractElement(Constant *Vec, Constant *Idx,

llvm/include/llvm/IR/GEPNoWrapFlags.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===-- llvm/GEPNoWrapFlags.h - NoWrap flags for GEPs -----------*- 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+
// This file defines the nowrap flags for getelementptr operators.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_IR_GEPNOWRAPFLAGS_H
14+
#define LLVM_IR_GEPNOWRAPFLAGS_H
15+
16+
namespace llvm {
17+
18+
/// Represents flags for the getelementptr instruction/expression.
19+
/// The following flags are supported:
20+
/// * inbounds (implies nusw)
21+
/// * nusw (no unsigned signed wrap)
22+
/// * nuw (no unsigned wrap)
23+
/// See LangRef for a description of their semantics.
24+
class GEPNoWrapFlags {
25+
enum : unsigned {
26+
InBoundsFlag = (1 << 0),
27+
NUSWFlag = (1 << 1),
28+
NUWFlag = (1 << 2),
29+
};
30+
31+
unsigned Flags;
32+
GEPNoWrapFlags(unsigned Flags) : Flags(Flags) {
33+
assert((!isInBounds() || hasNoUnsignedSignedWrap()) &&
34+
"inbounds implies nusw");
35+
}
36+
37+
public:
38+
GEPNoWrapFlags() : Flags(0) {}
39+
// For historical reasons, interpret plain boolean as InBounds.
40+
// TODO: Migrate users to pass explicit GEPNoWrapFlags and remove this ctor.
41+
GEPNoWrapFlags(bool IsInBounds)
42+
: Flags(IsInBounds ? (InBoundsFlag | NUSWFlag) : 0) {}
43+
44+
static GEPNoWrapFlags none() { return GEPNoWrapFlags(); }
45+
static GEPNoWrapFlags inBounds() {
46+
return GEPNoWrapFlags(InBoundsFlag | NUSWFlag);
47+
}
48+
static GEPNoWrapFlags noUnsignedSignedWrap() {
49+
return GEPNoWrapFlags(NUSWFlag);
50+
}
51+
static GEPNoWrapFlags noUnsignedWrap() { return GEPNoWrapFlags(NUWFlag); }
52+
53+
static GEPNoWrapFlags fromRaw(unsigned Flags) {
54+
return GEPNoWrapFlags(Flags);
55+
}
56+
unsigned getRaw() const { return Flags; }
57+
58+
bool isInBounds() const { return Flags & InBoundsFlag; }
59+
bool hasNoUnsignedSignedWrap() const { return Flags & NUSWFlag; }
60+
bool hasNoUnsignedWrap() const { return Flags & NUWFlag; }
61+
62+
GEPNoWrapFlags withoutInBounds() const {
63+
return GEPNoWrapFlags(Flags & ~InBoundsFlag);
64+
}
65+
GEPNoWrapFlags withoutNoUnsignedSignedWrap() const {
66+
return GEPNoWrapFlags(Flags & ~(InBoundsFlag | NUSWFlag));
67+
}
68+
GEPNoWrapFlags withoutNoUnsignedWrap() const {
69+
return GEPNoWrapFlags(Flags & ~NUWFlag);
70+
}
71+
72+
bool operator==(GEPNoWrapFlags Other) const { return Flags == Other.Flags; }
73+
bool operator!=(GEPNoWrapFlags Other) const { return !(*this == Other); }
74+
75+
GEPNoWrapFlags operator&(GEPNoWrapFlags Other) const {
76+
return GEPNoWrapFlags(Flags & Other.Flags);
77+
}
78+
GEPNoWrapFlags operator|(GEPNoWrapFlags Other) const {
79+
return GEPNoWrapFlags(Flags | Other.Flags);
80+
}
81+
GEPNoWrapFlags &operator&=(GEPNoWrapFlags Other) {
82+
Flags &= Other.Flags;
83+
return *this;
84+
}
85+
GEPNoWrapFlags &operator|=(GEPNoWrapFlags Other) {
86+
Flags |= Other.Flags;
87+
return *this;
88+
}
89+
};
90+
91+
} // end namespace llvm
92+
93+
#endif // LLVM_IR_GEPNOWRAPFLAGS_H

llvm/include/llvm/IR/Instructions.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/IR/CFG.h"
2727
#include "llvm/IR/Constant.h"
2828
#include "llvm/IR/DerivedTypes.h"
29+
#include "llvm/IR/GEPNoWrapFlags.h"
2930
#include "llvm/IR/InstrTypes.h"
3031
#include "llvm/IR/Instruction.h"
3132
#include "llvm/IR/OperandTraits.h"
@@ -1167,13 +1168,26 @@ class GetElementPtrInst : public Instruction {
11671168
/// a constant offset between them.
11681169
bool hasAllConstantIndices() const;
11691170

1171+
/// Set nowrap flags for GEP instruction.
1172+
void setNoWrapFlags(GEPNoWrapFlags NW);
1173+
11701174
/// Set or clear the inbounds flag on this GEP instruction.
11711175
/// See LangRef.html for the meaning of inbounds on a getelementptr.
1176+
/// TODO: Remove this method in favor of setNoWrapFlags().
11721177
void setIsInBounds(bool b = true);
11731178

1179+
/// Get the nowrap flags for the GEP instruction.
1180+
GEPNoWrapFlags getNoWrapFlags() const;
1181+
11741182
/// Determine whether the GEP has the inbounds flag.
11751183
bool isInBounds() const;
11761184

1185+
/// Determine whether the GEP has the nusw flag.
1186+
bool hasNoUnsignedSignedWrap() const;
1187+
1188+
/// Determine whether the GEP has the nuw flag.
1189+
bool hasNoUnsignedWrap() const;
1190+
11771191
/// Accumulate the constant address offset of this GEP if possible.
11781192
///
11791193
/// This routine accepts an APInt into which it will accumulate the constant

llvm/include/llvm/IR/Operator.h

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/ADT/MapVector.h"
1818
#include "llvm/IR/Constants.h"
1919
#include "llvm/IR/FMF.h"
20+
#include "llvm/IR/GEPNoWrapFlags.h"
2021
#include "llvm/IR/Instruction.h"
2122
#include "llvm/IR/Type.h"
2223
#include "llvm/IR/Value.h"
@@ -399,26 +400,24 @@ class LShrOperator
399400
};
400401

401402
class GEPOperator
402-
: public ConcreteOperator<Operator, Instruction::GetElementPtr> {
403-
friend class GetElementPtrInst;
404-
friend class ConstantExpr;
405-
406-
enum {
407-
IsInBounds = (1 << 0),
408-
};
409-
410-
void setIsInBounds(bool B) {
411-
SubclassOptionalData =
412-
(SubclassOptionalData & ~IsInBounds) | (B * IsInBounds);
413-
}
414-
403+
: public ConcreteOperator<Operator, Instruction::GetElementPtr> {
415404
public:
416405
/// Transparently provide more efficient getOperand methods.
417406
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
418407

408+
GEPNoWrapFlags getNoWrapFlags() const {
409+
return GEPNoWrapFlags::fromRaw(SubclassOptionalData);
410+
}
411+
419412
/// Test whether this is an inbounds GEP, as defined by LangRef.html.
420-
bool isInBounds() const {
421-
return SubclassOptionalData & IsInBounds;
413+
bool isInBounds() const { return getNoWrapFlags().isInBounds(); }
414+
415+
bool hasNoUnsignedSignedWrap() const {
416+
return getNoWrapFlags().hasNoUnsignedSignedWrap();
417+
}
418+
419+
bool hasNoUnsignedWrap() const {
420+
return getNoWrapFlags().hasNoUnsignedWrap();
422421
}
423422

424423
/// Returns the offset of the index with an inrange attachment, or

llvm/lib/Analysis/ConstantFolding.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
10051005
return C;
10061006

10071007
return ConstantExpr::getGetElementPtr(SrcElemTy, Ops[0], Ops.slice(1),
1008-
GEP->isInBounds(), GEP->getInRange());
1008+
GEP->getNoWrapFlags(),
1009+
GEP->getInRange());
10091010
}
10101011

10111012
if (auto *CE = dyn_cast<ConstantExpr>(InstOrCE)) {

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ lltok::Kind LLLexer::LexIdentifier() {
566566
KEYWORD(fast);
567567
KEYWORD(nuw);
568568
KEYWORD(nsw);
569+
KEYWORD(nusw);
569570
KEYWORD(exact);
570571
KEYWORD(disjoint);
571572
KEYWORD(inbounds);

0 commit comments

Comments
 (0)