Skip to content

Commit 92a2965

Browse files
committed
[Analysis] Add Scalable field in MemoryLocation.h
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 5317912 commit 92a2965

File tree

8 files changed

+173
-49
lines changed

8 files changed

+173
-49
lines changed

llvm/include/llvm/Analysis/MemoryLocation.h

Lines changed: 29 additions & 12 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,
71-
MapEmpty = BeforeOrAfterPointer - 2,
72-
MapTombstone = BeforeOrAfterPointer - 3,
72+
ScalableBit = uint64_t(1) << 62,
73+
AfterPointer = (BeforeOrAfterPointer - 1) & ~ScalableBit,
74+
MapEmpty = (BeforeOrAfterPointer - 2) & ~ScalableBit,
75+
MapTombstone = (BeforeOrAfterPointer - 3) & ~ScalableBit,
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;
@@ -88,6 +91,7 @@ class LocationSize {
8891
"AfterPointer is imprecise by definition.");
8992
static_assert(BeforeOrAfterPointer & ImpreciseBit,
9093
"BeforeOrAfterPointer is imprecise by definition.");
94+
static_assert(~(MaxValue & ScalableBit), "Max value don't have bit 62 set");
9195

9296
public:
9397
// FIXME: Migrate all users to construct via either `precise` or `upperBound`,
@@ -98,12 +102,17 @@ class LocationSize {
98102
// this assumes the provided value is precise.
99103
constexpr LocationSize(uint64_t Raw)
100104
: Value(Raw > MaxValue ? AfterPointer : Raw) {}
101-
102-
static LocationSize precise(uint64_t Value) { return LocationSize(Value); }
105+
constexpr LocationSize(uint64_t Raw, bool Scalable)
106+
: Value(Raw > MaxValue ? AfterPointer
107+
: Raw | (Scalable ? ScalableBit : uint64_t(0))) {}
108+
109+
// Make construction of LocationSize that takes in uint64_t to set Scalable
110+
// information as false
111+
static LocationSize precise(uint64_t Value) {
112+
return LocationSize(Value, false /*Scalable*/);
113+
}
103114
static LocationSize precise(TypeSize Value) {
104-
if (Value.isScalable())
105-
return afterPointer();
106-
return precise(Value.getFixedValue());
115+
return LocationSize(Value.getKnownMinValue(), Value.isScalable());
107116
}
108117

109118
static LocationSize upperBound(uint64_t Value) {
@@ -150,16 +159,22 @@ class LocationSize {
150159
return beforeOrAfterPointer();
151160
if (Value == AfterPointer || Other.Value == AfterPointer)
152161
return afterPointer();
162+
if (isScalable() || Other.isScalable())
163+
return afterPointer();
153164

154165
return upperBound(std::max(getValue(), Other.getValue()));
155166
}
156167

157168
bool hasValue() const {
158169
return Value != AfterPointer && Value != BeforeOrAfterPointer;
159170
}
160-
uint64_t getValue() const {
171+
bool isScalable() const { return (Value & ScalableBit); }
172+
173+
TypeSize getValue() const {
161174
assert(hasValue() && "Getting value from an unknown LocationSize!");
162-
return Value & ~ImpreciseBit;
175+
assert((Value & ~(ImpreciseBit | ScalableBit)) < MaxValue &&
176+
"Scalable bit of value should be masked");
177+
return {Value & ~(ImpreciseBit | ScalableBit), isScalable()};
163178
}
164179

165180
// Returns whether or not this value is precise. Note that if a value is
@@ -169,7 +184,9 @@ class LocationSize {
169184
}
170185

171186
// Convenience method to check if this LocationSize's value is 0.
172-
bool isZero() const { return hasValue() && getValue() == 0; }
187+
bool isZero() const {
188+
return hasValue() && getValue().getKnownMinValue() == 0;
189+
}
173190

174191
/// Whether accesses before the base pointer are possible.
175192
bool mayBeBeforePointer() const { return Value == BeforeOrAfterPointer; }

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,23 @@ bool BasicAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA,
101101
//===----------------------------------------------------------------------===//
102102

103103
/// Returns the size of the object specified by V or UnknownSize if unknown.
104-
static uint64_t getObjectSize(const Value *V, const DataLayout &DL,
105-
const TargetLibraryInfo &TLI,
106-
bool NullIsValidLoc,
107-
bool RoundToAlign = false) {
104+
/// getObjectSize does not support scalable Value
105+
static LocationSize getObjectSize(const Value *V, const DataLayout &DL,
106+
const TargetLibraryInfo &TLI,
107+
bool NullIsValidLoc,
108+
bool RoundToAlign = false) {
108109
uint64_t Size;
109110
ObjectSizeOpts Opts;
110111
Opts.RoundToAlign = RoundToAlign;
111112
Opts.NullIsUnknownSize = NullIsValidLoc;
112113
if (getObjectSize(V, Size, DL, &TLI, Opts))
113-
return Size;
114-
return MemoryLocation::UnknownSize;
114+
return LocationSize(Size);
115+
return LocationSize(MemoryLocation::UnknownSize);
115116
}
116117

117118
/// Returns true if we can prove that the object specified by V is smaller than
118119
/// Size.
119-
static bool isObjectSmallerThan(const Value *V, uint64_t Size,
120+
static bool isObjectSmallerThan(const Value *V, LocationSize Size,
120121
const DataLayout &DL,
121122
const TargetLibraryInfo &TLI,
122123
bool NullIsValidLoc) {
@@ -151,19 +152,21 @@ static bool isObjectSmallerThan(const Value *V, uint64_t Size,
151152

152153
// This function needs to use the aligned object size because we allow
153154
// reads a bit past the end given sufficient alignment.
154-
uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc,
155-
/*RoundToAlign*/ true);
155+
LocationSize ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc,
156+
/*RoundToAlign*/ true);
156157

157-
return ObjectSize != MemoryLocation::UnknownSize && ObjectSize < Size;
158+
// Bail on comparing V and Size if Size is scalable
159+
return ObjectSize != MemoryLocation::UnknownSize && !Size.isScalable() &&
160+
ObjectSize.getValue() < Size.getValue();
158161
}
159162

160163
/// Return the minimal extent from \p V to the end of the underlying object,
161164
/// assuming the result is used in an aliasing query. E.g., we do use the query
162165
/// location size and the fact that null pointers cannot alias here.
163-
static uint64_t getMinimalExtentFrom(const Value &V,
164-
const LocationSize &LocSize,
165-
const DataLayout &DL,
166-
bool NullIsValidLoc) {
166+
static LocationSize getMinimalExtentFrom(const Value &V,
167+
const LocationSize &LocSize,
168+
const DataLayout &DL,
169+
bool NullIsValidLoc) {
167170
// If we have dereferenceability information we know a lower bound for the
168171
// extent as accesses for a lower offset would be valid. We need to exclude
169172
// the "or null" part if null is a valid pointer. We can ignore frees, as an
@@ -175,15 +178,16 @@ static uint64_t getMinimalExtentFrom(const Value &V,
175178
// If queried with a precise location size, we assume that location size to be
176179
// accessed, thus valid.
177180
if (LocSize.isPrecise())
178-
DerefBytes = std::max(DerefBytes, LocSize.getValue());
179-
return DerefBytes;
181+
DerefBytes = std::max(DerefBytes, LocSize.getValue().getKnownMinValue());
182+
return LocationSize(DerefBytes, LocSize.isScalable());
180183
}
181184

182185
/// Returns true if we can prove that the object specified by V has size Size.
183-
static bool isObjectSize(const Value *V, uint64_t Size, const DataLayout &DL,
186+
static bool isObjectSize(const Value *V, TypeSize Size, const DataLayout &DL,
184187
const TargetLibraryInfo &TLI, bool NullIsValidLoc) {
185-
uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc);
186-
return ObjectSize != MemoryLocation::UnknownSize && ObjectSize == Size;
188+
LocationSize ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc);
189+
return ObjectSize != MemoryLocation::UnknownSize &&
190+
ObjectSize.getValue() == Size;
187191
}
188192

189193
//===----------------------------------------------------------------------===//
@@ -1055,15 +1059,19 @@ AliasResult BasicAAResult::aliasGEP(
10551059

10561060
// If an inbounds GEP would have to start from an out of bounds address
10571061
// for the two to alias, then we can assume noalias.
1062+
// TODO: Remove !isScalable() once BasicAA fully support scalable location
1063+
// size
10581064
if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
1059-
V2Size.hasValue() && DecompGEP1.Offset.sge(V2Size.getValue()) &&
1065+
V2Size.hasValue() && !V2Size.isScalable() &&
1066+
DecompGEP1.Offset.sge(V2Size.getValue()) &&
10601067
isBaseOfObject(DecompGEP2.Base))
10611068
return AliasResult::NoAlias;
10621069

10631070
if (isa<GEPOperator>(V2)) {
10641071
// Symmetric case to above.
10651072
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1066-
V1Size.hasValue() && DecompGEP1.Offset.sle(-V1Size.getValue()) &&
1073+
V1Size.hasValue() && !V1Size.isScalable() &&
1074+
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
10671075
isBaseOfObject(DecompGEP1.Base))
10681076
return AliasResult::NoAlias;
10691077
}
@@ -1087,6 +1095,10 @@ AliasResult BasicAAResult::aliasGEP(
10871095
return BaseAlias;
10881096
}
10891097

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

llvm/lib/Analysis/MemoryDependenceAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,8 @@ bool MemoryDependenceResults::getNonLocalPointerDepFromBB(
10681068
// be conservative.
10691069
ThrowOutEverything =
10701070
CacheInfo->Size.isPrecise() != Loc.Size.isPrecise() ||
1071-
CacheInfo->Size.getValue() < Loc.Size.getValue();
1071+
TypeSize::isKnownLT(CacheInfo->Size.getValue(),
1072+
Loc.Size.getValue());
10721073
} else {
10731074
// For our purposes, unknown size > all others.
10741075
ThrowOutEverything = !Loc.Size.hasValue();

llvm/lib/CodeGen/StackProtector.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ static bool HasAddressTaken(const Instruction *AI, TypeSize AllocSize,
176176
const auto *I = cast<Instruction>(U);
177177
// If this instruction accesses memory make sure it doesn't access beyond
178178
// the bounds of the allocated object.
179+
// TODO: TypeSize::getFixed should be modified to adapt to scalable vectors
179180
std::optional<MemoryLocation> MemLoc = MemoryLocation::getOrNone(I);
180181
if (MemLoc && MemLoc->Size.hasValue() &&
181-
!TypeSize::isKnownGE(AllocSize,
182-
TypeSize::getFixed(MemLoc->Size.getValue())))
182+
!TypeSize::isKnownGE(AllocSize, MemLoc->Size.getValue()))
183183
return true;
184184
switch (I->getOpcode()) {
185185
case Instruction::Store:

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

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

25332533
std::optional<MemoryLocation> Loc = MemoryLocation::getOrNone(I);
2534-
if (!Loc || Loc->Ptr != UseV || !Loc->Size.isPrecise() || I->isVolatile())
2534+
if (!Loc || Loc->Ptr != UseV || !Loc->Size.isPrecise() ||
2535+
Loc->Size.isScalable() || I->isVolatile())
25352536
return 0;
25362537

25372538
int64_t Offset;

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

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

208-
static uint64_t getPointerSize(const Value *V, const DataLayout &DL,
209-
const TargetLibraryInfo &TLI,
210-
const Function *F) {
208+
static LocationSize getPointerSize(const Value *V, const DataLayout &DL,
209+
const TargetLibraryInfo &TLI,
210+
const Function *F) {
211211
uint64_t Size;
212212
ObjectSizeOpts Opts;
213213
Opts.NullIsUnknownSize = NullPointerIsDefined(F);
214214

215215
if (getObjectSize(V, Size, DL, &TLI, Opts))
216-
return Size;
217-
return MemoryLocation::UnknownSize;
216+
return LocationSize(Size);
217+
return LocationSize(MemoryLocation::UnknownSize);
218218
}
219219

220220
namespace {
@@ -951,9 +951,10 @@ struct DSEState {
951951
// case the size/offset of the dead store does not matter.
952952
if (DeadUndObj == KillingUndObj && KillingLocSize.isPrecise() &&
953953
isIdentifiedObject(KillingUndObj)) {
954-
uint64_t KillingUndObjSize = getPointerSize(KillingUndObj, DL, TLI, &F);
955-
if (KillingUndObjSize != MemoryLocation::UnknownSize &&
956-
KillingUndObjSize == KillingLocSize.getValue())
954+
LocationSize KillingUndObjSize =
955+
getPointerSize(KillingUndObj, DL, TLI, &F);
956+
if (KillingUndObjSize.hasValue() &&
957+
KillingUndObjSize.getValue() == KillingLocSize.getValue())
957958
return OW_Complete;
958959
}
959960

@@ -976,22 +977,30 @@ struct DSEState {
976977
return isMaskedStoreOverwrite(KillingI, DeadI, BatchAA);
977978
}
978979

979-
const uint64_t KillingSize = KillingLocSize.getValue();
980-
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();
981986

987+
// TODO: Remove AnyScalable constraint once alias analysis fully support
988+
// scalable quantities
989+
if (AnyScalable)
990+
return OW_Unknown;
982991
// Query the alias information
983992
AliasResult AAR = BatchAA.alias(KillingLoc, DeadLoc);
984993

985994
// If the start pointers are the same, we just have to compare sizes to see if
986995
// the killing store was larger than the dead store.
987-
if (AAR == AliasResult::MustAlias) {
996+
if (AAR == AliasResult::MustAlias && !AnyScalable) {
988997
// Make sure that the KillingSize size is >= the DeadSize size.
989998
if (KillingSize >= DeadSize)
990999
return OW_Complete;
9911000
}
9921001

9931002
// If we hit a partial alias we may have a full overwrite
994-
if (AAR == AliasResult::PartialAlias && AAR.hasOffset()) {
1003+
if (AAR == AliasResult::PartialAlias && AAR.hasOffset() & !AnyScalable) {
9951004
int32_t Off = AAR.getOffset();
9961005
if (Off >= 0 && (uint64_t)Off + DeadSize <= KillingSize)
9971006
return OW_Complete;
@@ -1039,6 +1048,9 @@ struct DSEState {
10391048
//
10401049
// We have to be careful here as *Off is signed while *.Size is unsigned.
10411050

1051+
if (AnyScalable)
1052+
return OW_Unknown;
1053+
10421054
// Check if the dead access starts "not before" the killing one.
10431055
if (DeadOff >= KillingOff) {
10441056
// If the dead access ends "not after" the killing access then the
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+
}

0 commit comments

Comments
 (0)