Skip to content

Commit 844c188

Browse files
authored
[LAA] Refine stride checks for SCEVs during dependence analysis. (#99577)
Update getDependenceDistanceStrideAndSize to reason about different combinations of strides directly and explicitly. Update getPtrStride to return 0 for invariant pointers. Then proceed by checking the strides. If either source or sink are not strided by a constant (i.e. not a non-wrapping AddRec) or invariant, the accesses may overlap with earlier or later iterations and we cannot generate runtime checks to disambiguate them. Otherwise they are either loop invariant or strided. In that case, we can generate a runtime check to disambiguate them. If both are strided by constants, we proceed as previously. This is an alternative to #99239 and also replaces additional checks if the underlying object is loop-invariant. Fixes #87189. PR: #99577
1 parent 67ad23f commit 844c188

File tree

7 files changed

+85
-100
lines changed

7 files changed

+85
-100
lines changed

llvm/include/llvm/Analysis/LoopAccessAnalysis.h

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,7 @@ class MemoryDepChecker {
200200
///
201201
/// Only checks sets with elements in \p CheckDeps.
202202
bool areDepsSafe(const DepCandidates &AccessSets,
203-
const MemAccessInfoList &CheckDeps,
204-
const DenseMap<Value *, SmallVector<const Value *, 16>>
205-
&UnderlyingObjects);
203+
const MemAccessInfoList &CheckDeps);
206204

207205
/// No memory dependence was encountered that would inhibit
208206
/// vectorization.
@@ -352,11 +350,8 @@ class MemoryDepChecker {
352350
/// element access it records this distance in \p MinDepDistBytes (if this
353351
/// distance is smaller than any other distance encountered so far).
354352
/// Otherwise, this function returns true signaling a possible dependence.
355-
Dependence::DepType
356-
isDependent(const MemAccessInfo &A, unsigned AIdx, const MemAccessInfo &B,
357-
unsigned BIdx,
358-
const DenseMap<Value *, SmallVector<const Value *, 16>>
359-
&UnderlyingObjects);
353+
Dependence::DepType isDependent(const MemAccessInfo &A, unsigned AIdx,
354+
const MemAccessInfo &B, unsigned BIdx);
360355

361356
/// Check whether the data dependence could prevent store-load
362357
/// forwarding.
@@ -393,11 +388,9 @@ class MemoryDepChecker {
393388
/// determined, or a struct containing (Distance, Stride, TypeSize, AIsWrite,
394389
/// BIsWrite).
395390
std::variant<Dependence::DepType, DepDistanceStrideAndSizeInfo>
396-
getDependenceDistanceStrideAndSize(
397-
const MemAccessInfo &A, Instruction *AInst, const MemAccessInfo &B,
398-
Instruction *BInst,
399-
const DenseMap<Value *, SmallVector<const Value *, 16>>
400-
&UnderlyingObjects);
391+
getDependenceDistanceStrideAndSize(const MemAccessInfo &A, Instruction *AInst,
392+
const MemAccessInfo &B,
393+
Instruction *BInst);
401394
};
402395

403396
class RuntimePointerChecking;
@@ -799,7 +792,8 @@ replaceSymbolicStrideSCEV(PredicatedScalarEvolution &PSE,
799792
Value *Ptr);
800793

801794
/// If the pointer has a constant stride return it in units of the access type
802-
/// size. Otherwise return std::nullopt.
795+
/// size. If the pointer is loop-invariant, return 0. Otherwise return
796+
/// std::nullopt.
803797
///
804798
/// Ensure that it does not wrap in the address space, assuming the predicate
805799
/// associated with \p PSE is true.

llvm/lib/Analysis/LoopAccessAnalysis.cpp

Lines changed: 55 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -725,11 +725,6 @@ class AccessAnalysis {
725725

726726
const MemAccessInfoList &getDependenciesToCheck() const { return CheckDeps; }
727727

728-
const DenseMap<Value *, SmallVector<const Value *, 16>> &
729-
getUnderlyingObjects() const {
730-
return UnderlyingObjects;
731-
}
732-
733728
private:
734729
typedef MapVector<MemAccessInfo, SmallSetVector<Type *, 1>> PtrAccessMap;
735730

@@ -1455,22 +1450,23 @@ static bool isNoWrapAddRec(Value *Ptr, const SCEVAddRecExpr *AR,
14551450
}
14561451

14571452
/// Check whether the access through \p Ptr has a constant stride.
1458-
std::optional<int64_t> llvm::getPtrStride(PredicatedScalarEvolution &PSE,
1459-
Type *AccessTy, Value *Ptr,
1460-
const Loop *Lp,
1461-
const DenseMap<Value *, const SCEV *> &StridesMap,
1462-
bool Assume, bool ShouldCheckWrap) {
1453+
std::optional<int64_t>
1454+
llvm::getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr,
1455+
const Loop *Lp,
1456+
const DenseMap<Value *, const SCEV *> &StridesMap,
1457+
bool Assume, bool ShouldCheckWrap) {
1458+
const SCEV *PtrScev = replaceSymbolicStrideSCEV(PSE, StridesMap, Ptr);
1459+
if (PSE.getSE()->isLoopInvariant(PtrScev, Lp))
1460+
return {0};
1461+
14631462
Type *Ty = Ptr->getType();
14641463
assert(Ty->isPointerTy() && "Unexpected non-ptr");
1465-
14661464
if (isa<ScalableVectorType>(AccessTy)) {
14671465
LLVM_DEBUG(dbgs() << "LAA: Bad stride - Scalable object: " << *AccessTy
14681466
<< "\n");
14691467
return std::nullopt;
14701468
}
14711469

1472-
const SCEV *PtrScev = replaceSymbolicStrideSCEV(PSE, StridesMap, Ptr);
1473-
14741470
const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(PtrScev);
14751471
if (Assume && !AR)
14761472
AR = PSE.getAsAddRec(Ptr);
@@ -1897,23 +1893,11 @@ static bool areStridedAccessesIndependent(uint64_t Distance, uint64_t Stride,
18971893
return ScaledDist % Stride;
18981894
}
18991895

1900-
/// Returns true if any of the underlying objects has a loop varying address,
1901-
/// i.e. may change in \p L.
1902-
static bool
1903-
isLoopVariantIndirectAddress(ArrayRef<const Value *> UnderlyingObjects,
1904-
ScalarEvolution &SE, const Loop *L) {
1905-
return any_of(UnderlyingObjects, [&SE, L](const Value *UO) {
1906-
return !SE.isLoopInvariant(SE.getSCEV(const_cast<Value *>(UO)), L);
1907-
});
1908-
}
1909-
19101896
std::variant<MemoryDepChecker::Dependence::DepType,
19111897
MemoryDepChecker::DepDistanceStrideAndSizeInfo>
19121898
MemoryDepChecker::getDependenceDistanceStrideAndSize(
19131899
const AccessAnalysis::MemAccessInfo &A, Instruction *AInst,
1914-
const AccessAnalysis::MemAccessInfo &B, Instruction *BInst,
1915-
const DenseMap<Value *, SmallVector<const Value *, 16>>
1916-
&UnderlyingObjects) {
1900+
const AccessAnalysis::MemAccessInfo &B, Instruction *BInst) {
19171901
const auto &DL = InnermostLoop->getHeader()->getDataLayout();
19181902
auto &SE = *PSE.getSE();
19191903
const auto &[APtr, AIsWrite] = A;
@@ -1931,39 +1915,30 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
19311915
BPtr->getType()->getPointerAddressSpace())
19321916
return MemoryDepChecker::Dependence::Unknown;
19331917

1934-
int64_t StrideAPtr =
1935-
getPtrStride(PSE, ATy, APtr, InnermostLoop, SymbolicStrides, true)
1936-
.value_or(0);
1937-
int64_t StrideBPtr =
1938-
getPtrStride(PSE, BTy, BPtr, InnermostLoop, SymbolicStrides, true)
1939-
.value_or(0);
1918+
std::optional<int64_t> StrideAPtr =
1919+
getPtrStride(PSE, ATy, APtr, InnermostLoop, SymbolicStrides, true, true);
1920+
std::optional<int64_t> StrideBPtr =
1921+
getPtrStride(PSE, BTy, BPtr, InnermostLoop, SymbolicStrides, true, true);
19401922

19411923
const SCEV *Src = PSE.getSCEV(APtr);
19421924
const SCEV *Sink = PSE.getSCEV(BPtr);
19431925

19441926
// If the induction step is negative we have to invert source and sink of the
19451927
// dependence when measuring the distance between them. We should not swap
19461928
// AIsWrite with BIsWrite, as their uses expect them in program order.
1947-
if (StrideAPtr < 0) {
1929+
if (StrideAPtr && *StrideAPtr < 0) {
19481930
std::swap(Src, Sink);
19491931
std::swap(AInst, BInst);
1932+
std::swap(StrideAPtr, StrideBPtr);
19501933
}
19511934

19521935
const SCEV *Dist = SE.getMinusSCEV(Sink, Src);
19531936

19541937
LLVM_DEBUG(dbgs() << "LAA: Src Scev: " << *Src << "Sink Scev: " << *Sink
1955-
<< "(Induction step: " << StrideAPtr << ")\n");
1938+
<< "\n");
19561939
LLVM_DEBUG(dbgs() << "LAA: Distance for " << *AInst << " to " << *BInst
19571940
<< ": " << *Dist << "\n");
19581941

1959-
// Needs accesses where the addresses of the accessed underlying objects do
1960-
// not change within the loop.
1961-
if (isLoopVariantIndirectAddress(UnderlyingObjects.find(APtr)->second, SE,
1962-
InnermostLoop) ||
1963-
isLoopVariantIndirectAddress(UnderlyingObjects.find(BPtr)->second, SE,
1964-
InnermostLoop))
1965-
return MemoryDepChecker::Dependence::IndirectUnsafe;
1966-
19671942
// Check if we can prove that Sink only accesses memory after Src's end or
19681943
// vice versa. At the moment this is limited to cases where either source or
19691944
// sink are loop invariant to avoid compile-time increases. This is not
@@ -1985,12 +1960,33 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
19851960
}
19861961
}
19871962

1988-
// Need accesses with constant strides and the same direction. We don't want
1989-
// to vectorize "A[B[i]] += ..." and similar code or pointer arithmetic that
1990-
// could wrap in the address space.
1991-
if (!StrideAPtr || !StrideBPtr || (StrideAPtr > 0 && StrideBPtr < 0) ||
1992-
(StrideAPtr < 0 && StrideBPtr > 0)) {
1963+
// Need accesses with constant strides and the same direction for further
1964+
// dependence analysis. We don't want to vectorize "A[B[i]] += ..." and
1965+
// similar code or pointer arithmetic that could wrap in the address space.
1966+
1967+
// If either Src or Sink are not strided (i.e. not a non-wrapping AddRec) and
1968+
// not loop-invariant (stride will be 0 in that case), we cannot analyze the
1969+
// dependence further and also cannot generate runtime checks.
1970+
if (!StrideAPtr || !StrideBPtr) {
19931971
LLVM_DEBUG(dbgs() << "Pointer access with non-constant stride\n");
1972+
return MemoryDepChecker::Dependence::IndirectUnsafe;
1973+
}
1974+
1975+
int64_t StrideAPtrInt = *StrideAPtr;
1976+
int64_t StrideBPtrInt = *StrideBPtr;
1977+
LLVM_DEBUG(dbgs() << "LAA: Src induction step: " << StrideAPtrInt
1978+
<< " Sink induction step: " << StrideBPtrInt << "\n");
1979+
// At least Src or Sink are loop invariant and the other is strided or
1980+
// invariant. We can generate a runtime check to disambiguate the accesses.
1981+
if (StrideAPtrInt == 0 || StrideBPtrInt == 0)
1982+
return MemoryDepChecker::Dependence::Unknown;
1983+
1984+
// Both Src and Sink have a constant stride, check if they are in the same
1985+
// direction.
1986+
if ((StrideAPtrInt > 0 && StrideBPtrInt < 0) ||
1987+
(StrideAPtrInt < 0 && StrideBPtrInt > 0)) {
1988+
LLVM_DEBUG(
1989+
dbgs() << "Pointer access with strides in different directions\n");
19941990
return MemoryDepChecker::Dependence::Unknown;
19951991
}
19961992

@@ -1999,22 +1995,20 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
19991995
DL.getTypeStoreSizeInBits(ATy) == DL.getTypeStoreSizeInBits(BTy);
20001996
if (!HasSameSize)
20011997
TypeByteSize = 0;
2002-
return DepDistanceStrideAndSizeInfo(Dist, std::abs(StrideAPtr),
2003-
std::abs(StrideBPtr), TypeByteSize,
1998+
return DepDistanceStrideAndSizeInfo(Dist, std::abs(StrideAPtrInt),
1999+
std::abs(StrideBPtrInt), TypeByteSize,
20042000
AIsWrite, BIsWrite);
20052001
}
20062002

2007-
MemoryDepChecker::Dependence::DepType MemoryDepChecker::isDependent(
2008-
const MemAccessInfo &A, unsigned AIdx, const MemAccessInfo &B,
2009-
unsigned BIdx,
2010-
const DenseMap<Value *, SmallVector<const Value *, 16>>
2011-
&UnderlyingObjects) {
2003+
MemoryDepChecker::Dependence::DepType
2004+
MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
2005+
const MemAccessInfo &B, unsigned BIdx) {
20122006
assert(AIdx < BIdx && "Must pass arguments in program order");
20132007

20142008
// Get the dependence distance, stride, type size and what access writes for
20152009
// the dependence between A and B.
2016-
auto Res = getDependenceDistanceStrideAndSize(
2017-
A, InstMap[AIdx], B, InstMap[BIdx], UnderlyingObjects);
2010+
auto Res =
2011+
getDependenceDistanceStrideAndSize(A, InstMap[AIdx], B, InstMap[BIdx]);
20182012
if (std::holds_alternative<Dependence::DepType>(Res))
20192013
return std::get<Dependence::DepType>(Res);
20202014

@@ -2248,10 +2242,8 @@ MemoryDepChecker::Dependence::DepType MemoryDepChecker::isDependent(
22482242
return Dependence::BackwardVectorizable;
22492243
}
22502244

2251-
bool MemoryDepChecker::areDepsSafe(
2252-
const DepCandidates &AccessSets, const MemAccessInfoList &CheckDeps,
2253-
const DenseMap<Value *, SmallVector<const Value *, 16>>
2254-
&UnderlyingObjects) {
2245+
bool MemoryDepChecker::areDepsSafe(const DepCandidates &AccessSets,
2246+
const MemAccessInfoList &CheckDeps) {
22552247

22562248
MinDepDistBytes = -1;
22572249
SmallPtrSet<MemAccessInfo, 8> Visited;
@@ -2294,8 +2286,8 @@ bool MemoryDepChecker::areDepsSafe(
22942286
if (*I1 > *I2)
22952287
std::swap(A, B);
22962288

2297-
Dependence::DepType Type = isDependent(*A.first, A.second, *B.first,
2298-
B.second, UnderlyingObjects);
2289+
Dependence::DepType Type =
2290+
isDependent(*A.first, A.second, *B.first, B.second);
22992291
mergeInStatus(Dependence::isSafeForVectorization(Type));
23002292

23012293
// Gather dependences unless we accumulated MaxDependences
@@ -2650,8 +2642,7 @@ bool LoopAccessInfo::analyzeLoop(AAResults *AA, const LoopInfo *LI,
26502642
if (Accesses.isDependencyCheckNeeded()) {
26512643
LLVM_DEBUG(dbgs() << "LAA: Checking memory dependencies\n");
26522644
DepsAreSafe = DepChecker->areDepsSafe(DependentAccesses,
2653-
Accesses.getDependenciesToCheck(),
2654-
Accesses.getUnderlyingObjects());
2645+
Accesses.getDependenciesToCheck());
26552646

26562647
if (!DepsAreSafe && DepChecker->shouldRetryWithRuntimeCheck()) {
26572648
LLVM_DEBUG(dbgs() << "LAA: Retrying with memory checks\n");

llvm/test/Analysis/LoopAccessAnalysis/load-store-index-loaded-in-loop.ll

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,19 @@
99
define void @B_indices_loaded_in_loop_A_stored(ptr %A, ptr noalias %B, i64 %N, i64 %off) {
1010
; CHECK-LABEL: 'B_indices_loaded_in_loop_A_stored'
1111
; CHECK-NEXT: loop:
12-
; CHECK-NEXT: Memory dependences are safe with run-time checks
12+
; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
13+
; CHECK-NEXT: Unsafe indirect dependence.
1314
; CHECK-NEXT: Dependences:
15+
; CHECK-NEXT: IndirectUnsafe:
16+
; CHECK-NEXT: %l = load i32, ptr %gep.B, align 4 ->
17+
; CHECK-NEXT: store i32 %inc, ptr %gep.B, align 4
18+
; CHECK-EMPTY:
19+
; CHECK-NEXT: Unknown:
20+
; CHECK-NEXT: %indices = load i8, ptr %gep.A, align 1 ->
21+
; CHECK-NEXT: store i32 %l, ptr %gep.C, align 4
22+
; CHECK-EMPTY:
1423
; CHECK-NEXT: Run-time memory checks:
15-
; CHECK-NEXT: Check 0:
16-
; CHECK-NEXT: Comparing group ([[GRP1:0x[0-9a-f]+]]):
17-
; CHECK-NEXT: %gep.C = getelementptr inbounds i32, ptr %A, i64 %iv
18-
; CHECK-NEXT: Against group ([[GRP2:0x[0-9a-f]+]]):
19-
; CHECK-NEXT: %gep.A = getelementptr inbounds i8, ptr %A, i64 %iv.off
2024
; CHECK-NEXT: Grouped accesses:
21-
; CHECK-NEXT: Group [[GRP1]]:
22-
; CHECK-NEXT: (Low: %A High: ((4 * %N) + %A))
23-
; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop>
24-
; CHECK-NEXT: Group [[GRP2]]:
25-
; CHECK-NEXT: (Low: (%off + %A) High: (%N + %off + %A))
26-
; CHECK-NEXT: Member: {(%off + %A),+,1}<nw><%loop>
2725
; CHECK-EMPTY:
2826
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
2927
; CHECK-NEXT: SCEV assumptions:
@@ -59,9 +57,9 @@ define void @B_indices_loaded_in_loop_A_not_stored(ptr %A, ptr noalias %B, i64 %
5957
; CHECK-LABEL: 'B_indices_loaded_in_loop_A_not_stored'
6058
; CHECK-NEXT: loop:
6159
; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
62-
; CHECK-NEXT: Unknown data dependence.
60+
; CHECK-NEXT: Unsafe indirect dependence.
6361
; CHECK-NEXT: Dependences:
64-
; CHECK-NEXT: Unknown:
62+
; CHECK-NEXT: IndirectUnsafe:
6563
; CHECK-NEXT: %l = load i32, ptr %gep.B, align 4 ->
6664
; CHECK-NEXT: store i32 %inc, ptr %gep.B, align 4
6765
; CHECK-EMPTY:

llvm/test/Analysis/LoopAccessAnalysis/pointer-with-unknown-bounds.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
1313
; CHECK-NEXT: for.body:
1414
; CHECK-NEXT: Report: unsafe dependent memory operations in loop
1515
; CHECK-NOT: Report: cannot identify array bounds
16-
; CHECK-NEXT: Unknown data dependence.
16+
; CHECK-NEXT: Unsafe indirect dependence.
1717
; CHECK-NEXT: Dependences:
18-
; CHECK-NEXT: Unknown:
18+
; CHECK-NEXT: IndirectUnsafe:
1919
; CHECK-NEXT: %loadA = load i16, ptr %arrayidxA, align 2 ->
2020
; CHECK-NEXT: store i16 %mul, ptr %arrayidxA, align 2
2121

llvm/test/Analysis/LoopAccessAnalysis/print-order.ll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
; CHECK-LABEL: 'negative_step'
1010
; CHECK: LAA: Found an analyzable loop: loop
1111
; CHECK: LAA: Checking memory dependencies
12-
; CHECK-NEXT: LAA: Src Scev: {(4092 + %A),+,-4}<nw><%loop>Sink Scev: {(4088 + %A)<nuw>,+,-4}<nw><%loop>(Induction step: -1)
12+
; CHECK-NEXT: LAA: Src Scev: {(4092 + %A),+,-4}<nw><%loop>Sink Scev: {(4088 + %A)<nuw>,+,-4}<nw><%loop>
1313
; CHECK-NEXT: LAA: Distance for store i32 %add, ptr %gep.A.plus.1, align 4 to %l = load i32, ptr %gep.A, align 4: -4
14+
; CHECK-NEXT: LAA: Src induction step: -1 Sink induction step: -1
1415
; CHECK-NEXT: LAA: Dependence is negative
1516

1617
define void @negative_step(ptr nocapture %A) {
@@ -41,8 +42,9 @@ exit:
4142
; CHECK-LABEL: 'positive_step'
4243
; CHECK: LAA: Found an analyzable loop: loop
4344
; CHECK: LAA: Checking memory dependencies
44-
; CHECK-NEXT: LAA: Src Scev: {(4 + %A)<nuw>,+,4}<nuw><%loop>Sink Scev: {%A,+,4}<nw><%loop>(Induction step: 1)
45+
; CHECK-NEXT: LAA: Src Scev: {(4 + %A)<nuw>,+,4}<nuw><%loop>Sink Scev: {%A,+,4}<nw><%loop>
4546
; CHECK-NEXT: LAA: Distance for %l = load i32, ptr %gep.A, align 4 to store i32 %add, ptr %gep.A.minus.1, align 4: -4
47+
; CHECK-NEXT: LAA: Src induction step: 1 Sink induction step: 1
4648
; CHECK-NEXT: LAA: Dependence is negative
4749

4850
define void @positive_step(ptr nocapture %A) {

llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ define void @test(ptr noalias %x, ptr noalias %y, ptr noalias %z) {
55
; CHECK-LABEL: 'test'
66
; CHECK-NEXT: loop:
77
; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
8-
; CHECK-NEXT: Unknown data dependence.
8+
; CHECK-NEXT: Unsafe indirect dependence.
99
; CHECK-NEXT: Dependences:
10-
; CHECK-NEXT: Unknown:
10+
; CHECK-NEXT: IndirectUnsafe:
1111
; CHECK-NEXT: %load = load double, ptr %gep.sel, align 8 ->
1212
; CHECK-NEXT: store double %load, ptr %gep.sel2, align 8
1313
; CHECK-EMPTY:

llvm/test/Analysis/LoopAccessAnalysis/symbolic-stride.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ define void @single_stride_used_for_trip_count(ptr noalias %A, ptr noalias %B, i
276276
; CHECK-LABEL: 'single_stride_used_for_trip_count'
277277
; CHECK-NEXT: loop:
278278
; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
279-
; CHECK-NEXT: Unknown data dependence.
279+
; CHECK-NEXT: Unsafe indirect dependence.
280280
; CHECK-NEXT: Dependences:
281-
; CHECK-NEXT: Unknown:
281+
; CHECK-NEXT: IndirectUnsafe:
282282
; CHECK-NEXT: %load = load i32, ptr %gep.A, align 4 ->
283283
; CHECK-NEXT: store i32 %add, ptr %gep.A.next, align 4
284284
; CHECK-EMPTY:

0 commit comments

Comments
 (0)