Skip to content

Commit 211dc4a

Browse files
[Analysis] Add Scalable field in MemoryLocation.h (#69716)
This is the first of a series of patch to improve Alias Analysis on Scalable quantities. Keep Scalable information from TypeSize which will be used in Alias Analysis.
1 parent 86b4388 commit 211dc4a

File tree

8 files changed

+152
-44
lines changed

8 files changed

+152
-44
lines changed

llvm/include/llvm/Analysis/MemoryLocation.h

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,19 @@ class Value;
6464
//
6565
// If asked to represent a pathologically large value, this will degrade to
6666
// std::nullopt.
67+
// Store Scalable information in bit 62 of Value. Scalable information is
68+
// required to do Alias Analysis on Scalable quantities
6769
class LocationSize {
6870
enum : uint64_t {
6971
BeforeOrAfterPointer = ~uint64_t(0),
70-
AfterPointer = BeforeOrAfterPointer - 1,
72+
ScalableBit = uint64_t(1) << 62,
73+
AfterPointer = (BeforeOrAfterPointer - 1) & ~ScalableBit,
7174
MapEmpty = BeforeOrAfterPointer - 2,
7275
MapTombstone = BeforeOrAfterPointer - 3,
7376
ImpreciseBit = uint64_t(1) << 63,
7477

7578
// The maximum value we can represent without falling back to 'unknown'.
76-
MaxValue = (MapTombstone - 1) & ~ImpreciseBit,
79+
MaxValue = (MapTombstone - 1) & ~(ImpreciseBit | ScalableBit),
7780
};
7881

7982
uint64_t Value;
@@ -82,12 +85,16 @@ class LocationSize {
8285
// public LocationSize ctor goes away.
8386
enum DirectConstruction { Direct };
8487

85-
constexpr LocationSize(uint64_t Raw, DirectConstruction): Value(Raw) {}
88+
constexpr LocationSize(uint64_t Raw, DirectConstruction) : Value(Raw) {}
89+
constexpr LocationSize(uint64_t Raw, bool Scalable)
90+
: Value(Raw > MaxValue ? AfterPointer
91+
: Raw | (Scalable ? ScalableBit : uint64_t(0))) {}
8692

8793
static_assert(AfterPointer & ImpreciseBit,
8894
"AfterPointer is imprecise by definition.");
8995
static_assert(BeforeOrAfterPointer & ImpreciseBit,
9096
"BeforeOrAfterPointer is imprecise by definition.");
97+
static_assert(~(MaxValue & ScalableBit), "Max value don't have bit 62 set");
9198

9299
public:
93100
// FIXME: Migrate all users to construct via either `precise` or `upperBound`,
@@ -98,12 +105,12 @@ class LocationSize {
98105
// this assumes the provided value is precise.
99106
constexpr LocationSize(uint64_t Raw)
100107
: Value(Raw > MaxValue ? AfterPointer : Raw) {}
101-
102-
static LocationSize precise(uint64_t Value) { return LocationSize(Value); }
108+
// Create non-scalable LocationSize
109+
static LocationSize precise(uint64_t Value) {
110+
return LocationSize(Value, false /*Scalable*/);
111+
}
103112
static LocationSize precise(TypeSize Value) {
104-
if (Value.isScalable())
105-
return afterPointer();
106-
return precise(Value.getFixedValue());
113+
return LocationSize(Value.getKnownMinValue(), Value.isScalable());
107114
}
108115

109116
static LocationSize upperBound(uint64_t Value) {
@@ -150,26 +157,32 @@ class LocationSize {
150157
return beforeOrAfterPointer();
151158
if (Value == AfterPointer || Other.Value == AfterPointer)
152159
return afterPointer();
160+
if (isScalable() || Other.isScalable())
161+
return afterPointer();
153162

154163
return upperBound(std::max(getValue(), Other.getValue()));
155164
}
156165

157166
bool hasValue() const {
158167
return Value != AfterPointer && Value != BeforeOrAfterPointer;
159168
}
160-
uint64_t getValue() const {
169+
bool isScalable() const { return (Value & ScalableBit); }
170+
171+
TypeSize getValue() const {
161172
assert(hasValue() && "Getting value from an unknown LocationSize!");
162-
return Value & ~ImpreciseBit;
173+
assert((Value & ~(ImpreciseBit | ScalableBit)) < MaxValue &&
174+
"Scalable bit of value should be masked");
175+
return {Value & ~(ImpreciseBit | ScalableBit), isScalable()};
163176
}
164177

165178
// Returns whether or not this value is precise. Note that if a value is
166179
// precise, it's guaranteed to not be unknown.
167-
bool isPrecise() const {
168-
return (Value & ImpreciseBit) == 0;
169-
}
180+
bool isPrecise() const { return (Value & ImpreciseBit) == 0; }
170181

171182
// Convenience method to check if this LocationSize's value is 0.
172-
bool isZero() const { return hasValue() && getValue() == 0; }
183+
bool isZero() const {
184+
return hasValue() && getValue().getKnownMinValue() == 0;
185+
}
173186

174187
/// Whether accesses before the base pointer are possible.
175188
bool mayBeBeforePointer() const { return Value == BeforeOrAfterPointer; }
@@ -178,9 +191,7 @@ class LocationSize {
178191
return Value == Other.Value;
179192
}
180193

181-
bool operator!=(const LocationSize &Other) const {
182-
return !(*this == Other);
183-
}
194+
bool operator!=(const LocationSize &Other) const { return !(*this == Other); }
184195

185196
// Ordering operators are not provided, since it's unclear if there's only one
186197
// reasonable way to compare:
@@ -317,9 +328,7 @@ class MemoryLocation {
317328

318329
// Specialize DenseMapInfo.
319330
template <> struct DenseMapInfo<LocationSize> {
320-
static inline LocationSize getEmptyKey() {
321-
return LocationSize::mapEmpty();
322-
}
331+
static inline LocationSize getEmptyKey() { return LocationSize::mapEmpty(); }
323332
static inline LocationSize getTombstoneKey() {
324333
return LocationSize::mapTombstone();
325334
}
@@ -349,6 +358,6 @@ template <> struct DenseMapInfo<MemoryLocation> {
349358
return LHS == RHS;
350359
}
351360
};
352-
}
361+
} // namespace llvm
353362

354363
#endif

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ bool BasicAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA,
100100
// Useful predicates
101101
//===----------------------------------------------------------------------===//
102102

103-
/// Returns the size of the object specified by V or nullopt if unknown.
104-
static std::optional<uint64_t> getObjectSize(const Value *V,
103+
/// Returns the size of the object specified by V or UnknownSize if unknown.
104+
static std::optional<TypeSize> getObjectSize(const Value *V,
105105
const DataLayout &DL,
106106
const TargetLibraryInfo &TLI,
107107
bool NullIsValidLoc,
@@ -111,13 +111,13 @@ static std::optional<uint64_t> getObjectSize(const Value *V,
111111
Opts.RoundToAlign = RoundToAlign;
112112
Opts.NullIsUnknownSize = NullIsValidLoc;
113113
if (getObjectSize(V, Size, DL, &TLI, Opts))
114-
return Size;
114+
return TypeSize::getFixed(Size);
115115
return std::nullopt;
116116
}
117117

118118
/// Returns true if we can prove that the object specified by V is smaller than
119119
/// Size.
120-
static bool isObjectSmallerThan(const Value *V, uint64_t Size,
120+
static bool isObjectSmallerThan(const Value *V, TypeSize Size,
121121
const DataLayout &DL,
122122
const TargetLibraryInfo &TLI,
123123
bool NullIsValidLoc) {
@@ -152,16 +152,16 @@ static bool isObjectSmallerThan(const Value *V, uint64_t Size,
152152

153153
// This function needs to use the aligned object size because we allow
154154
// reads a bit past the end given sufficient alignment.
155-
std::optional<uint64_t> ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc,
155+
std::optional<TypeSize> ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc,
156156
/*RoundToAlign*/ true);
157157

158-
return ObjectSize && *ObjectSize < Size;
158+
return ObjectSize && TypeSize::isKnownLT(*ObjectSize, Size);
159159
}
160160

161161
/// Return the minimal extent from \p V to the end of the underlying object,
162162
/// assuming the result is used in an aliasing query. E.g., we do use the query
163163
/// location size and the fact that null pointers cannot alias here.
164-
static uint64_t getMinimalExtentFrom(const Value &V,
164+
static TypeSize getMinimalExtentFrom(const Value &V,
165165
const LocationSize &LocSize,
166166
const DataLayout &DL,
167167
bool NullIsValidLoc) {
@@ -176,14 +176,14 @@ static uint64_t getMinimalExtentFrom(const Value &V,
176176
// If queried with a precise location size, we assume that location size to be
177177
// accessed, thus valid.
178178
if (LocSize.isPrecise())
179-
DerefBytes = std::max(DerefBytes, LocSize.getValue());
180-
return DerefBytes;
179+
DerefBytes = std::max(DerefBytes, LocSize.getValue().getKnownMinValue());
180+
return TypeSize::getFixed(DerefBytes);
181181
}
182182

183183
/// Returns true if we can prove that the object specified by V has size Size.
184-
static bool isObjectSize(const Value *V, uint64_t Size, const DataLayout &DL,
184+
static bool isObjectSize(const Value *V, TypeSize Size, const DataLayout &DL,
185185
const TargetLibraryInfo &TLI, bool NullIsValidLoc) {
186-
std::optional<uint64_t> ObjectSize =
186+
std::optional<TypeSize> ObjectSize =
187187
getObjectSize(V, DL, TLI, NullIsValidLoc);
188188
return ObjectSize && *ObjectSize == Size;
189189
}
@@ -1058,15 +1058,19 @@ AliasResult BasicAAResult::aliasGEP(
10581058

10591059
// If an inbounds GEP would have to start from an out of bounds address
10601060
// for the two to alias, then we can assume noalias.
1061+
// TODO: Remove !isScalable() once BasicAA fully support scalable location
1062+
// size
10611063
if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
1062-
V2Size.hasValue() && DecompGEP1.Offset.sge(V2Size.getValue()) &&
1064+
V2Size.hasValue() && !V2Size.isScalable() &&
1065+
DecompGEP1.Offset.sge(V2Size.getValue()) &&
10631066
isBaseOfObject(DecompGEP2.Base))
10641067
return AliasResult::NoAlias;
10651068

10661069
if (isa<GEPOperator>(V2)) {
10671070
// Symmetric case to above.
10681071
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1069-
V1Size.hasValue() && DecompGEP1.Offset.sle(-V1Size.getValue()) &&
1072+
V1Size.hasValue() && !V1Size.isScalable() &&
1073+
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
10701074
isBaseOfObject(DecompGEP1.Base))
10711075
return AliasResult::NoAlias;
10721076
}
@@ -1090,6 +1094,10 @@ AliasResult BasicAAResult::aliasGEP(
10901094
return BaseAlias;
10911095
}
10921096

1097+
// Bail on analysing scalable LocationSize
1098+
if (V1Size.isScalable() || V2Size.isScalable())
1099+
return AliasResult::MayAlias;
1100+
10931101
// If there is a constant difference between the pointers, but the difference
10941102
// is less than the size of the associated memory object, then we know
10951103
// that the objects are partially overlapping. If the difference is

llvm/lib/Analysis/MemoryDependenceAnalysis.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,10 @@ static bool canSkipClobberingStore(const StoreInst *SI,
373373
return false;
374374
if (MemoryLocation::get(SI).Size != MemLoc.Size)
375375
return false;
376-
if (std::min(MemLocAlign, SI->getAlign()).value() < MemLoc.Size.getValue())
376+
if (MemLoc.Size.isScalable())
377+
return false;
378+
if (std::min(MemLocAlign, SI->getAlign()).value() <
379+
MemLoc.Size.getValue().getKnownMinValue())
377380
return false;
378381

379382
auto *LI = dyn_cast<LoadInst>(SI->getValueOperand());
@@ -1099,7 +1102,8 @@ bool MemoryDependenceResults::getNonLocalPointerDepFromBB(
10991102
// be conservative.
11001103
ThrowOutEverything =
11011104
CacheInfo->Size.isPrecise() != Loc.Size.isPrecise() ||
1102-
CacheInfo->Size.getValue() < Loc.Size.getValue();
1105+
!TypeSize::isKnownGE(CacheInfo->Size.getValue(),
1106+
Loc.Size.getValue());
11031107
} else {
11041108
// For our purposes, unknown size > all others.
11051109
ThrowOutEverything = !Loc.Size.hasValue();

llvm/lib/CodeGen/StackProtector.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,7 @@ static bool HasAddressTaken(const Instruction *AI, TypeSize AllocSize,
178178
// the bounds of the allocated object.
179179
std::optional<MemoryLocation> MemLoc = MemoryLocation::getOrNone(I);
180180
if (MemLoc && MemLoc->Size.hasValue() &&
181-
!TypeSize::isKnownGE(AllocSize,
182-
TypeSize::getFixed(MemLoc->Size.getValue())))
181+
!TypeSize::isKnownGE(AllocSize, MemLoc->Size.getValue()))
183182
return true;
184183
switch (I->getOpcode()) {
185184
case Instruction::Store:

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2546,7 +2546,8 @@ static int64_t getKnownNonNullAndDerefBytesForUse(
25462546
}
25472547

25482548
std::optional<MemoryLocation> Loc = MemoryLocation::getOrNone(I);
2549-
if (!Loc || Loc->Ptr != UseV || !Loc->Size.isPrecise() || I->isVolatile())
2549+
if (!Loc || Loc->Ptr != UseV || !Loc->Size.isPrecise() ||
2550+
Loc->Size.isScalable() || I->isVolatile())
25502551
return 0;
25512552

25522553
int64_t Offset;

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ static bool isShortenableAtTheBeginning(Instruction *I) {
205205
return isa<AnyMemSetInst>(I);
206206
}
207207

208-
static std::optional<uint64_t> getPointerSize(const Value *V,
208+
static std::optional<TypeSize> getPointerSize(const Value *V,
209209
const DataLayout &DL,
210210
const TargetLibraryInfo &TLI,
211211
const Function *F) {
@@ -214,7 +214,7 @@ static std::optional<uint64_t> getPointerSize(const Value *V,
214214
Opts.NullIsUnknownSize = NullPointerIsDefined(F);
215215

216216
if (getObjectSize(V, Size, DL, &TLI, Opts))
217-
return Size;
217+
return TypeSize::getFixed(Size);
218218
return std::nullopt;
219219
}
220220

@@ -952,7 +952,7 @@ struct DSEState {
952952
// case the size/offset of the dead store does not matter.
953953
if (DeadUndObj == KillingUndObj && KillingLocSize.isPrecise() &&
954954
isIdentifiedObject(KillingUndObj)) {
955-
std::optional<uint64_t> KillingUndObjSize =
955+
std::optional<TypeSize> KillingUndObjSize =
956956
getPointerSize(KillingUndObj, DL, TLI, &F);
957957
if (KillingUndObjSize && *KillingUndObjSize == KillingLocSize.getValue())
958958
return OW_Complete;
@@ -977,9 +977,15 @@ struct DSEState {
977977
return isMaskedStoreOverwrite(KillingI, DeadI, BatchAA);
978978
}
979979

980-
const uint64_t KillingSize = KillingLocSize.getValue();
981-
const uint64_t DeadSize = DeadLoc.Size.getValue();
980+
const TypeSize KillingSize = KillingLocSize.getValue();
981+
const TypeSize DeadSize = DeadLoc.Size.getValue();
982+
// Bail on doing Size comparison which depends on AA for now
983+
// TODO: Remove AnyScalable once Alias Analysis deal with scalable vectors
984+
const bool AnyScalable =
985+
DeadSize.isScalable() || KillingLocSize.isScalable();
982986

987+
if (AnyScalable)
988+
return OW_Unknown;
983989
// Query the alias information
984990
AliasResult AAR = BatchAA.alias(KillingLoc, DeadLoc);
985991

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S < %s -passes=print-alias-sets 2>&1 | FileCheck %s
3+
4+
; CHECK-LABEL: Alias sets for function 'sn'
5+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %p, unknown after)
6+
define void @sn(ptr %p) {;
7+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
8+
store i64 0, ptr %p, align 2
9+
ret void
10+
}
11+
12+
; CHECK-LABEL: Alias sets for function 'ns'
13+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %p, unknown after)
14+
define void @ns(ptr %p) {
15+
store i64 0, ptr %p, align 2
16+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
17+
ret void
18+
}
19+
20+
; CHECK-LABEL: Alias sets for function 'ss':
21+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %p, LocationSize::precise(vscale x 16))
22+
define void @ss(ptr %p) {
23+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
24+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
25+
ret void
26+
}
27+
28+
; CHECK-LABEL: Alias sets for function 'ss2':
29+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %p, unknown after)
30+
define void @ss2(ptr %p) {
31+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
32+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
33+
store <vscale x 4 x i64> zeroinitializer, ptr %p, align 2
34+
ret void
35+
}
36+
; CHECK-LABEL: Alias sets for function 'son':
37+
; CHECK: AliasSet[{{.*}}, 2] may alias, Mod Pointers: (ptr %g, LocationSize::precise(vscale x 16)), (ptr %p, LocationSize::precise(8))
38+
define void @son(ptr %p) {
39+
%g = getelementptr i8, ptr %p, i64 8
40+
store <vscale x 2 x i64> zeroinitializer, ptr %g, align 2
41+
store i64 0, ptr %p, align 2
42+
ret void
43+
}
44+
45+
; CHECK-LABEL: Alias sets for function 'sno':
46+
; CHECK: AliasSet[{{.*}}, 2] may alias, Mod Pointers: (ptr %p, LocationSize::precise(vscale x 16)), (ptr %g, LocationSize::precise(8))
47+
define void @sno(ptr %p) {
48+
%g = getelementptr i8, ptr %p, i64 8
49+
store <vscale x 2 x i64> zeroinitializer, ptr %p, align 2
50+
store i64 0, ptr %g, align 2
51+
ret void
52+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S < %s -passes=gvn | FileCheck %s
3+
4+
define void @test(i1 %cmp19, ptr %p) {
5+
; CHECK-LABEL: @test(
6+
; CHECK-NEXT: entry:
7+
; CHECK-NEXT: br i1 [[CMP19:%.*]], label [[WHILE_BODY_LR_PH:%.*]], label [[FOR_COND_PREHEADER:%.*]]
8+
; CHECK: while.body.lr.ph:
9+
; CHECK-NEXT: [[DOTPRE1:%.*]] = load <vscale x 2 x double>, ptr [[P:%.*]], align 16
10+
; CHECK-NEXT: [[TMP0:%.*]] = extractelement <vscale x 2 x double> [[DOTPRE1]], i64 0
11+
; CHECK-NEXT: ret void
12+
; CHECK: for.cond.preheader:
13+
; CHECK-NEXT: [[DOTPRE:%.*]] = load double, ptr [[P]], align 8
14+
; CHECK-NEXT: [[ADD:%.*]] = fadd double [[DOTPRE]], 0.000000e+00
15+
; CHECK-NEXT: ret void
16+
;
17+
entry:
18+
br i1 %cmp19, label %while.body.lr.ph, label %for.cond.preheader
19+
20+
while.body.lr.ph: ; preds = %entry
21+
%.pre1 = load <vscale x 2 x double>, ptr %p, align 16
22+
%0 = extractelement <vscale x 2 x double> %.pre1, i64 0
23+
ret void
24+
25+
for.cond.preheader: ; preds = %entry
26+
%.pre = load double, ptr %p, align 8
27+
%add = fadd double %.pre, 0.000000e+00
28+
ret void
29+
}

0 commit comments

Comments
 (0)