Skip to content

Commit bc476c1

Browse files
committed
[BasicAA] Add Vscale GEP decomposition on variable index
Enable BasicAA to be done on Scalable GEP & LocationSize Scalable GEP expression such as @llvm.vscale and GEP of scalable type are attached to the VariableGEPIndex, with Val representing Vscale. VScale AA works if there's only one variable index (the vscale) and constant offsets in the GEP for now
1 parent f2c09e5 commit bc476c1

File tree

4 files changed

+224
-98
lines changed

4 files changed

+224
-98
lines changed

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 150 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "llvm/IR/IntrinsicInst.h"
4545
#include "llvm/IR/Intrinsics.h"
4646
#include "llvm/IR/Operator.h"
47+
#include "llvm/IR/PatternMatch.h"
4748
#include "llvm/IR/Type.h"
4849
#include "llvm/IR/User.h"
4950
#include "llvm/IR/Value.h"
@@ -63,6 +64,7 @@
6364
#define DEBUG_TYPE "basicaa"
6465

6566
using namespace llvm;
67+
using namespace llvm::PatternMatch;
6668

6769
/// Enable analysis of recursive PHI nodes.
6870
static cl::opt<bool> EnableRecPhiAnalysis("basic-aa-recphi", cl::Hidden,
@@ -344,13 +346,20 @@ struct LinearExpression {
344346

345347
/// Analyzes the specified value as a linear expression: "A*V + B", where A and
346348
/// B are constant integers.
347-
static LinearExpression GetLinearExpression(
348-
const CastedValue &Val, const DataLayout &DL, unsigned Depth,
349-
AssumptionCache *AC, DominatorTree *DT) {
349+
static LinearExpression GetLinearExpression(const CastedValue &Val,
350+
const DataLayout &DL,
351+
unsigned Depth, AssumptionCache *AC,
352+
DominatorTree *DT) {
350353
// Limit our recursion depth.
351354
if (Depth == 6)
352355
return Val;
353356

357+
// If llvm.vscale is matched, set linear expression with scale 1 and offset 0
358+
if (match(Val.V, m_VScale())) {
359+
return LinearExpression(Val, APInt(Val.getBitWidth(), 1),
360+
APInt(Val.getBitWidth(), 0), true);
361+
}
362+
354363
if (const ConstantInt *Const = dyn_cast<ConstantInt>(Val.V))
355364
return LinearExpression(Val, APInt(Val.getBitWidth(), 0),
356365
Val.evaluateWith(Const->getValue()), true);
@@ -457,6 +466,9 @@ struct VariableGEPIndex {
457466
CastedValue Val;
458467
APInt Scale;
459468

469+
// A value representing vscale quantity in a GEP expression
470+
bool IsVScale;
471+
460472
// Context instruction to use when querying information about this index.
461473
const Instruction *CxtI;
462474

@@ -479,13 +491,10 @@ struct VariableGEPIndex {
479491
dbgs() << "\n";
480492
}
481493
void print(raw_ostream &OS) const {
482-
OS << "(V=" << Val.V->getName()
483-
<< ", zextbits=" << Val.ZExtBits
484-
<< ", sextbits=" << Val.SExtBits
485-
<< ", truncbits=" << Val.TruncBits
486-
<< ", scale=" << Scale
487-
<< ", nsw=" << IsNSW
488-
<< ", negated=" << IsNegated << ")";
494+
OS << "(V=" << Val.V->getName() << " IsVScale=" << IsVScale
495+
<< ", zextbits=" << Val.ZExtBits << ", sextbits=" << Val.SExtBits
496+
<< ", truncbits=" << Val.TruncBits << ", scale=" << Scale
497+
<< ", nsw=" << IsNSW << ", negated=" << IsNegated << ")";
489498
}
490499
};
491500
}
@@ -606,6 +615,7 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
606615
for (User::const_op_iterator I = GEPOp->op_begin() + 1, E = GEPOp->op_end();
607616
I != E; ++I, ++GTI) {
608617
const Value *Index = *I;
618+
const bool ScalableGEP = isa<ScalableVectorType>(GTI.getIndexedType());
609619
// Compute the (potentially symbolic) offset in bytes for this index.
610620
if (StructType *STy = GTI.getStructTypeOrNull()) {
611621
// For a struct, add the member offset.
@@ -617,27 +627,18 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
617627
continue;
618628
}
619629

630+
TypeSize AllocTypeSize = DL.getTypeAllocSize(GTI.getIndexedType());
620631
// For an array/pointer, add the element offset, explicitly scaled.
632+
// Skip adding to constant offset if GEP index is marked as scalable
633+
// they are handled below as variable offset
621634
if (const ConstantInt *CIdx = dyn_cast<ConstantInt>(Index)) {
622635
if (CIdx->isZero())
623636
continue;
624-
625-
// Don't attempt to analyze GEPs if the scalable index is not zero.
626-
TypeSize AllocTypeSize = DL.getTypeAllocSize(GTI.getIndexedType());
627-
if (AllocTypeSize.isScalable()) {
628-
Decomposed.Base = V;
629-
return Decomposed;
637+
if (!ScalableGEP) {
638+
Decomposed.Offset += AllocTypeSize.getFixedValue() *
639+
CIdx->getValue().sextOrTrunc(MaxIndexSize);
640+
continue;
630641
}
631-
632-
Decomposed.Offset += AllocTypeSize.getFixedValue() *
633-
CIdx->getValue().sextOrTrunc(MaxIndexSize);
634-
continue;
635-
}
636-
637-
TypeSize AllocTypeSize = DL.getTypeAllocSize(GTI.getIndexedType());
638-
if (AllocTypeSize.isScalable()) {
639-
Decomposed.Base = V;
640-
return Decomposed;
641642
}
642643

643644
GepHasConstantOffset = false;
@@ -647,22 +648,55 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
647648
unsigned Width = Index->getType()->getIntegerBitWidth();
648649
unsigned SExtBits = IndexSize > Width ? IndexSize - Width : 0;
649650
unsigned TruncBits = IndexSize < Width ? Width - IndexSize : 0;
650-
LinearExpression LE = GetLinearExpression(
651-
CastedValue(Index, 0, SExtBits, TruncBits), DL, 0, AC, DT);
651+
// Scalable GEP decomposition
652+
// Allow Scalable GEP to be decomposed in the case of
653+
// 1. getelementptr <4 x vscale x i32> with 1st index as a constant
654+
// 2. Index which have a leaf of @llvm.vscale
655+
// In both cases, essentially CastedValue of VariableGEPIndex is Vscale,
656+
// however in the 1st case, CastedValue is of type constant, hence another
657+
// flag in VariableGEPIndex is created in this case, IsVScale If GEP is
658+
// Scalable type, e.g. <4 x vscale x i32>, the first index will have
659+
// vscale as a variable index, create a LE in this case
660+
LinearExpression LE(CastedValue(Index, 0, SExtBits, TruncBits));
661+
if (ScalableGEP) {
662+
if (const ConstantInt *CIdx = dyn_cast<ConstantInt>(Index)) {
663+
LE = LinearExpression(
664+
CastedValue(Index, 0, SExtBits, TruncBits),
665+
CastedValue(Index, 0, SExtBits, TruncBits)
666+
.evaluateWith(CIdx->getValue()),
667+
APInt(CastedValue(Index, 0, SExtBits, TruncBits).getBitWidth(),
668+
0),
669+
true);
670+
assert(LE.Offset.isZero() && "For Scalable GEP constant first index, "
671+
"the offset of LE should be 0");
672+
} else {
673+
// if first index is not a constant, a single variable gep will
674+
// contain 2 variables, bail in this case
675+
Decomposed.Base = V;
676+
return Decomposed;
677+
}
678+
} else
679+
LE = GetLinearExpression(CastedValue(Index, 0, SExtBits, TruncBits), DL,
680+
0, AC, DT);
652681

653682
// Scale by the type size.
654-
unsigned TypeSize = AllocTypeSize.getFixedValue();
683+
unsigned TypeSize = AllocTypeSize.getKnownMinValue();
655684
LE = LE.mul(APInt(IndexSize, TypeSize), GEPOp->isInBounds());
656685
Decomposed.Offset += LE.Offset.sext(MaxIndexSize);
657686
APInt Scale = LE.Scale.sext(MaxIndexSize);
687+
bool LEhasVscale = match(LE.Val.V, m_VScale());
658688

659689
// If we already had an occurrence of this index variable, merge this
660690
// scale into it. For example, we want to handle:
661691
// A[x][x] -> x*16 + x*4 -> x*20
662692
// This also ensures that 'x' only appears in the index list once.
693+
// Only add to IsVScale VariableGEPIndex if it's @llvm.vscale or gep
694+
// vscale index
663695
for (unsigned i = 0, e = Decomposed.VarIndices.size(); i != e; ++i) {
664-
if (Decomposed.VarIndices[i].Val.V == LE.Val.V &&
665-
Decomposed.VarIndices[i].Val.hasSameCastsAs(LE.Val)) {
696+
if (Decomposed.VarIndices[i].Val.hasSameCastsAs(LE.Val) &&
697+
((Decomposed.VarIndices[i].IsVScale &&
698+
(ScalableGEP || LEhasVscale)) ||
699+
Decomposed.VarIndices[i].Val.V == LE.Val.V)) {
666700
Scale += Decomposed.VarIndices[i].Scale;
667701
LE.IsNSW = false; // We cannot guarantee nsw for the merge.
668702
Decomposed.VarIndices.erase(Decomposed.VarIndices.begin() + i);
@@ -672,10 +706,21 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
672706

673707
// Make sure that we have a scale that makes sense for this target's
674708
// index size.
709+
// Only allow variableGEP decomposition for constants, in the case of
710+
// vscale
675711
Scale = adjustToIndexSize(Scale, IndexSize);
712+
bool InvalidVarVScale = (ScalableGEP && LEhasVscale) ||
713+
(ScalableGEP && !isa<ConstantInt>(LE.Val.V));
714+
715+
assert(!InvalidVarVScale &&
716+
"Variable GEP index contains VScale and another variable");
676717

677718
if (!!Scale) {
678-
VariableGEPIndex Entry = {LE.Val, Scale, CxtI, LE.IsNSW,
719+
VariableGEPIndex Entry = {LE.Val,
720+
Scale,
721+
ScalableGEP || LEhasVscale,
722+
CxtI,
723+
LE.IsNSW,
679724
/* IsNegated */ false};
680725
Decomposed.VarIndices.push_back(Entry);
681726
}
@@ -1058,19 +1103,15 @@ AliasResult BasicAAResult::aliasGEP(
10581103

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

10691111
if (isa<GEPOperator>(V2)) {
10701112
// Symmetric case to above.
10711113
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
1072-
V1Size.hasValue() && !V1Size.isScalable() &&
1073-
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
1114+
V1Size.hasValue() && DecompGEP1.Offset.sle(-V1Size.getValue().getKnownMinValue()) &&
10741115
isBaseOfObject(DecompGEP1.Base))
10751116
return AliasResult::NoAlias;
10761117
}
@@ -1094,10 +1135,6 @@ AliasResult BasicAAResult::aliasGEP(
10941135
return BaseAlias;
10951136
}
10961137

1097-
// Bail on analysing scalable LocationSize
1098-
if (V1Size.isScalable() || V2Size.isScalable())
1099-
return AliasResult::MayAlias;
1100-
11011138
// If there is a constant difference between the pointers, but the difference
11021139
// is less than the size of the associated memory object, then we know
11031140
// that the objects are partially overlapping. If the difference is
@@ -1124,16 +1161,16 @@ AliasResult BasicAAResult::aliasGEP(
11241161
Off = -Off;
11251162
}
11261163

1127-
if (!VLeftSize.hasValue())
1164+
if (!VLeftSize.hasValue() || VLeftSize.isScalable())
11281165
return AliasResult::MayAlias;
11291166

1130-
const uint64_t LSize = VLeftSize.getValue();
1167+
const uint64_t LSize = VLeftSize.getValue().getKnownMinValue();
11311168
if (Off.ult(LSize)) {
11321169
// Conservatively drop processing if a phi was visited and/or offset is
11331170
// too big.
11341171
AliasResult AR = AliasResult::PartialAlias;
11351172
if (VRightSize.hasValue() && Off.ule(INT32_MAX) &&
1136-
(Off + VRightSize.getValue()).ule(LSize)) {
1173+
(Off + VRightSize.getValue().getKnownMinValue()).ule(LSize)) {
11371174
// Memory referenced by right pointer is nested. Save the offset in
11381175
// cache. Note that originally offset estimated as GEP1-V2, but
11391176
// AliasResult contains the shift that represents GEP1+Offset=V2.
@@ -1149,12 +1186,69 @@ AliasResult BasicAAResult::aliasGEP(
11491186
if (!V1Size.hasValue() || !V2Size.hasValue())
11501187
return AliasResult::MayAlias;
11511188

1189+
// VScale Alias Analysis
1190+
// GEPs with Vscale will have the expression A*Vscale + B (1 variable index and constant offset)
1191+
// The difference between two GEPs and Scalable LocationSize can then be analysed as they have the form of
1192+
// LSize SubtractDecomposedGEP output
1193+
// A * Vscale B * Vscale + C
1194+
// Since VScale is strictly a positive number (Vscale >= 1), the larger GEP can be known
1195+
// TODO: Use knowledge of vscale_range to make the analysis more accurate
1196+
if (DecompGEP1.VarIndices.size() == 1 && DecompGEP1.VarIndices[0].IsVScale &&
1197+
(V1Size.isScalable() || V2Size.isScalable())) {
1198+
const VariableGEPIndex &ScalableVar = DecompGEP1.VarIndices[0];
1199+
bool StrictlyPos = false, StrictlyNeg = false;
1200+
APInt &Off = DecompGEP1.Offset;
1201+
if (!ScalableVar.IsNegated) {
1202+
if (Off.isNegative())
1203+
StrictlyPos = ScalableVar.Scale.ugt(Off.abs());
1204+
else
1205+
StrictlyPos = true;
1206+
} else
1207+
StrictlyPos = Off.isNonNegative();
1208+
1209+
if (ScalableVar.IsNegated) {
1210+
if (Off.isNonNegative())
1211+
StrictlyNeg = Off.ult(ScalableVar.Scale.abs());
1212+
else
1213+
StrictlyNeg = true;
1214+
} else
1215+
StrictlyNeg = Off.isNegative();
1216+
1217+
if (StrictlyPos || StrictlyNeg) {
1218+
LocationSize VLeftSize = V2Size;
1219+
LocationSize VRightSize = V1Size;
1220+
const bool Swapped = StrictlyNeg;
1221+
1222+
if (Swapped) {
1223+
std::swap(VLeftSize, VRightSize);
1224+
Off = -Off;
1225+
}
1226+
1227+
const uint64_t LSize = VLeftSize.getValue().getKnownMinValue();
1228+
if (VLeftSize.isScalable() && ScalableVar.Scale.ult(LSize) &&
1229+
(ScalableVar.Scale + DecompGEP1.Offset).ult(LSize))
1230+
return AliasResult::PartialAlias;
1231+
1232+
if ((ScalableVar.Scale.uge(LSize) && VLeftSize.isScalable()) ||
1233+
((ScalableVar.Scale + DecompGEP1.Offset).uge(LSize) &&
1234+
!VLeftSize.isScalable()))
1235+
return AliasResult::NoAlias;
1236+
}
1237+
}
1238+
1239+
// Bail on Scalable location size from now onwards
1240+
if (V1Size.isScalable() || V2Size.isScalable())
1241+
return AliasResult::MayAlias;
1242+
11521243
APInt GCD;
11531244
ConstantRange OffsetRange = ConstantRange(DecompGEP1.Offset);
11541245
for (unsigned i = 0, e = DecompGEP1.VarIndices.size(); i != e; ++i) {
11551246
const VariableGEPIndex &Index = DecompGEP1.VarIndices[i];
11561247
const APInt &Scale = Index.Scale;
11571248
APInt ScaleForGCD = Scale;
1249+
assert((!Index.IsVScale || match(Index.Val.V, m_VScale()) ||
1250+
isa<ConstantInt>(Index.Val.V)) &&
1251+
"Not allowed to have non-constant values if IsVScale is set");
11581252
if (!Index.IsNSW)
11591253
ScaleForGCD =
11601254
APInt::getOneBitSet(Scale.getBitWidth(), Scale.countr_zero());
@@ -1727,7 +1821,12 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
17271821
bool Found = false;
17281822
for (auto I : enumerate(DestGEP.VarIndices)) {
17291823
VariableGEPIndex &Dest = I.value();
1730-
if (!isValueEqualInPotentialCycles(Dest.Val.V, Src.Val.V, AAQI) ||
1824+
if (Dest.IsVScale != Src.IsVScale)
1825+
continue;
1826+
const bool SrcDestAreVScale = Dest.IsVScale && Src.IsVScale;
1827+
// Suppress base value checks if Src and Dst are of constant VScale
1828+
if ((!SrcDestAreVScale &&
1829+
!isValueEqualInPotentialCycles(Dest.Val.V, Src.Val.V, AAQI)) ||
17311830
!Dest.Val.hasSameCastsAs(Src.Val))
17321831
continue;
17331832

@@ -1752,7 +1851,11 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
17521851

17531852
// If we didn't consume this entry, add it to the end of the Dest list.
17541853
if (!Found) {
1755-
VariableGEPIndex Entry = {Src.Val, Src.Scale, Src.CxtI, Src.IsNSW,
1854+
VariableGEPIndex Entry = {Src.Val,
1855+
Src.Scale,
1856+
Src.IsVScale,
1857+
Src.CxtI,
1858+
Src.IsNSW,
17561859
/* IsNegated */ true};
17571860
DestGEP.VarIndices.push_back(Entry);
17581861
}

llvm/test/Analysis/AliasSet/memloc-vscale.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ define void @ss2(ptr %p) {
3434
ret void
3535
}
3636
; 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))
37+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %g, LocationSize::precise(vscale x 16))
38+
; CHECK: AliasSet[{{.*}}, 1] must alias, Mod Pointers: (ptr %p, LocationSize::precise(8))
3839
define void @son(ptr %p) {
3940
%g = getelementptr i8, ptr %p, i64 8
4041
store <vscale x 2 x i64> zeroinitializer, ptr %g, align 2

0 commit comments

Comments
 (0)