Skip to content

Commit be0340b

Browse files
committed
[LAA] Only use inbounds/nusw in isNoWrap if the GEP is dereferenced.
Update isNoWrap to only use the inbounds/nusw flags from GEPs that are guaranteed to be dereferenced on every iteration. This fixes a case where we incorrectly determine no dependence. I think the issue is isolated to code that evaluates the resulting AddRec at BTC, just using it the compute the distance between accesses should still be fine; if the access does not execute in a given iteration, there's no dependence in that iteration. But isolating the code is not straight-forward, so be conservative for now. The practical impact should be very minor (only one loop changed across a corpus with 27k modules from large C/C++ workloads. Fixes #160912.
1 parent a099c91 commit be0340b

File tree

6 files changed

+156
-39
lines changed

6 files changed

+156
-39
lines changed

llvm/include/llvm/Analysis/LoopAccessAnalysis.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,8 @@ getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr,
896896
const Loop *Lp,
897897
const DenseMap<Value *, const SCEV *> &StridesMap =
898898
DenseMap<Value *, const SCEV *>(),
899-
bool Assume = false, bool ShouldCheckWrap = true);
899+
bool Assume = false, bool ShouldCheckWrap = true,
900+
DominatorTree *DT = nullptr);
900901

901902
/// Returns the distance between the pointers \p PtrA and \p PtrB iff they are
902903
/// compatible and it is possible to calculate the distance between them. This

llvm/lib/Analysis/LoopAccessAnalysis.cpp

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -800,11 +800,11 @@ class AccessAnalysis {
800800
typedef SmallVector<MemAccessInfo, 8> MemAccessInfoList;
801801

802802
AccessAnalysis(const Loop *TheLoop, AAResults *AA, const LoopInfo *LI,
803-
MemoryDepChecker::DepCandidates &DA,
803+
DominatorTree &DT, MemoryDepChecker::DepCandidates &DA,
804804
PredicatedScalarEvolution &PSE,
805805
SmallPtrSetImpl<MDNode *> &LoopAliasScopes)
806-
: TheLoop(TheLoop), BAA(*AA), AST(BAA), LI(LI), DepCands(DA), PSE(PSE),
807-
LoopAliasScopes(LoopAliasScopes) {
806+
: TheLoop(TheLoop), BAA(*AA), AST(BAA), LI(LI), DT(DT), DepCands(DA),
807+
PSE(PSE), LoopAliasScopes(LoopAliasScopes) {
808808
// We're analyzing dependences across loop iterations.
809809
BAA.enableCrossIterationMode();
810810
}
@@ -928,6 +928,8 @@ class AccessAnalysis {
928928
/// The LoopInfo of the loop being checked.
929929
const LoopInfo *LI;
930930

931+
DominatorTree &DT;
932+
931933
/// Sets of potentially dependent accesses - members of one set share an
932934
/// underlying pointer. The set "CheckDeps" identfies which sets really need a
933935
/// dependence check.
@@ -1009,7 +1011,8 @@ getStrideFromAddRec(const SCEVAddRecExpr *AR, const Loop *Lp, Type *AccessTy,
10091011
/// informating from the IR pointer value to determine no-wrap.
10101012
static bool isNoWrap(PredicatedScalarEvolution &PSE, const SCEVAddRecExpr *AR,
10111013
Value *Ptr, Type *AccessTy, const Loop *L, bool Assume,
1012-
std::optional<int64_t> Stride = std::nullopt) {
1014+
std::optional<int64_t> Stride = std::nullopt,
1015+
DominatorTree *DT = nullptr) {
10131016
// FIXME: This should probably only return true for NUW.
10141017
if (AR->getNoWrapFlags(SCEV::NoWrapMask))
10151018
return true;
@@ -1021,9 +1024,22 @@ static bool isNoWrap(PredicatedScalarEvolution &PSE, const SCEVAddRecExpr *AR,
10211024
// the distance between the previously accessed location and the wrapped
10221025
// location will be larger than half the pointer index type space. In that
10231026
// case, the GEP would be poison and any memory access dependent on it would
1024-
// be immediate UB when executed.
1027+
// be immediate UB when executed. The reasoning can only be applied if the
1028+
// pointer is dereferenced at least at the last iteration. For now, check if
1029+
// it is dereferenced in every iteration.
10251030
if (auto *GEP = dyn_cast_if_present<GetElementPtrInst>(Ptr);
1026-
GEP && GEP->hasNoUnsignedSignedWrap())
1031+
GEP && GEP->hasNoUnsignedSignedWrap() &&
1032+
(L->getHeader() == L->getLoopLatch() ||
1033+
(any_of(GEP->users(), [L, DT](User *U) {
1034+
if (!isa<LoadInst, StoreInst>(U))
1035+
return false;
1036+
BasicBlock *UserBB = cast<Instruction>(U)->getParent();
1037+
if (DT && !LoopAccessInfo::blockNeedsPredication(UserBB, L, DT))
1038+
return true;
1039+
return UserBB == L->getHeader() ||
1040+
(L->getExitingBlock() == L->getLoopLatch() &&
1041+
UserBB == L->getLoopLatch());
1042+
}))))
10271043
return true;
10281044

10291045
if (!Stride)
@@ -1287,7 +1303,7 @@ bool AccessAnalysis::createCheckForAccess(
12871303
}
12881304

12891305
if (!isNoWrap(PSE, AR, RTCheckPtrs.size() == 1 ? Ptr : nullptr, AccessTy,
1290-
TheLoop, Assume))
1306+
TheLoop, Assume, std::nullopt, &DT))
12911307
return false;
12921308
}
12931309

@@ -1602,7 +1618,7 @@ std::optional<int64_t>
16021618
llvm::getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr,
16031619
const Loop *Lp,
16041620
const DenseMap<Value *, const SCEV *> &StridesMap,
1605-
bool Assume, bool ShouldCheckWrap) {
1621+
bool Assume, bool ShouldCheckWrap, DominatorTree *DT) {
16061622
const SCEV *PtrScev = replaceSymbolicStrideSCEV(PSE, StridesMap, Ptr);
16071623
if (PSE.getSE()->isLoopInvariant(PtrScev, Lp))
16081624
return 0;
@@ -1624,7 +1640,7 @@ llvm::getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr,
16241640
if (!ShouldCheckWrap || !Stride)
16251641
return Stride;
16261642

1627-
if (isNoWrap(PSE, AR, Ptr, AccessTy, Lp, Assume, Stride))
1643+
if (isNoWrap(PSE, AR, Ptr, AccessTy, Lp, Assume, Stride, DT))
16281644
return Stride;
16291645

16301646
LLVM_DEBUG(
@@ -2041,10 +2057,10 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
20412057
BPtr->getType()->getPointerAddressSpace())
20422058
return MemoryDepChecker::Dependence::Unknown;
20432059

2044-
std::optional<int64_t> StrideAPtr =
2045-
getPtrStride(PSE, ATy, APtr, InnermostLoop, SymbolicStrides, true, true);
2046-
std::optional<int64_t> StrideBPtr =
2047-
getPtrStride(PSE, BTy, BPtr, InnermostLoop, SymbolicStrides, true, true);
2060+
std::optional<int64_t> StrideAPtr = getPtrStride(
2061+
PSE, ATy, APtr, InnermostLoop, SymbolicStrides, true, true, DT);
2062+
std::optional<int64_t> StrideBPtr = getPtrStride(
2063+
PSE, BTy, BPtr, InnermostLoop, SymbolicStrides, true, true, DT);
20482064

20492065
const SCEV *Src = PSE.getSCEV(APtr);
20502066
const SCEV *Sink = PSE.getSCEV(BPtr);
@@ -2621,7 +2637,8 @@ bool LoopAccessInfo::analyzeLoop(AAResults *AA, const LoopInfo *LI,
26212637
}
26222638

26232639
MemoryDepChecker::DepCandidates DepCands;
2624-
AccessAnalysis Accesses(TheLoop, AA, LI, DepCands, *PSE, LoopAliasScopes);
2640+
AccessAnalysis Accesses(TheLoop, AA, LI, *DT, DepCands, *PSE,
2641+
LoopAliasScopes);
26252642

26262643
// Holds the analyzed pointers. We don't want to call getUnderlyingObjects
26272644
// multiple times on the same object. If the ptr is accessed twice, once
@@ -2685,7 +2702,8 @@ bool LoopAccessInfo::analyzeLoop(AAResults *AA, const LoopInfo *LI,
26852702
bool IsReadOnlyPtr = false;
26862703
Type *AccessTy = getLoadStoreType(LD);
26872704
if (Seen.insert({Ptr, AccessTy}).second ||
2688-
!getPtrStride(*PSE, AccessTy, Ptr, TheLoop, SymbolicStrides)) {
2705+
!getPtrStride(*PSE, AccessTy, Ptr, TheLoop, SymbolicStrides, false,
2706+
true, DT)) {
26892707
++NumReads;
26902708
IsReadOnlyPtr = true;
26912709
}

llvm/lib/Analysis/VectorUtils.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,8 +1388,9 @@ void InterleavedAccessInfo::collectConstStrideAccesses(
13881388
// even without the transformation. The wrapping checks are therefore
13891389
// deferred until after we've formed the interleaved groups.
13901390
int64_t Stride =
1391-
getPtrStride(PSE, ElementTy, Ptr, TheLoop, Strides,
1392-
/*Assume=*/true, /*ShouldCheckWrap=*/false).value_or(0);
1391+
getPtrStride(PSE, ElementTy, Ptr, TheLoop, Strides,
1392+
/*Assume=*/true, /*ShouldCheckWrap=*/false, DT)
1393+
.value_or(0);
13931394

13941395
const SCEV *Scev = replaceSymbolicStrideSCEV(PSE, Strides, Ptr);
13951396
AccessStrideInfo[&I] = StrideDescriptor(Stride, Scev, Size,
@@ -1644,7 +1645,8 @@ void InterleavedAccessInfo::analyzeInterleaving(
16441645
Value *MemberPtr = getLoadStorePointerOperand(Member);
16451646
Type *AccessTy = getLoadStoreType(Member);
16461647
if (getPtrStride(PSE, AccessTy, MemberPtr, TheLoop, Strides,
1647-
/*Assume=*/false, /*ShouldCheckWrap=*/true).value_or(0))
1648+
/*Assume=*/false, /*ShouldCheckWrap=*/true, DT)
1649+
.value_or(0))
16481650
return false;
16491651
LLVM_DEBUG(dbgs() << "LV: Invalidate candidate interleaved group due to "
16501652
<< FirstOrLast

llvm/test/Analysis/LoopAccessAnalysis/inbounds-gep-in-predicated-blocks.ll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ define void @test_inbounds_gep_used_in_predicated_block(ptr %A, i64 %n) {
1919
; CHECK-NEXT: Dependences:
2020
; CHECK-NEXT: Run-time memory checks:
2121
; CHECK-NEXT: Grouped accesses:
22+
; CHECK-NEXT: Group GRP0:
23+
; CHECK-NEXT: (Low: %A High: (-4611686018427387705 + %A))
24+
; CHECK-NEXT: Member: {%A,+,4611686018427387906}<%loop.header>
25+
; CHECK-NEXT: Member: {%A,+,4611686018427387905}<%loop.header>
2226
; CHECK-EMPTY:
2327
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
2428
; CHECK-NEXT: SCEV assumptions:
29+
; CHECK-NEXT: {%A,+,4611686018427387906}<%loop.header> Added Flags: <nusw>
2530
; CHECK-EMPTY:
2631
; CHECK-NEXT: Expressions re-written:
2732
;

llvm/test/Transforms/LoopVectorize/RISCV/masked_gather_scatter.ll

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,33 @@ define void @foo4(ptr nocapture %A, ptr nocapture readonly %B, ptr nocapture rea
1717
; RV32-LABEL: @foo4(
1818
; RV32-NEXT: entry:
1919
; RV32-NEXT: br label [[VECTOR_MEMCHECK:%.*]]
20+
; RV32: vector.scevcheck:
21+
; RV32-NEXT: [[MUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 128, i32 624)
22+
; RV32-NEXT: [[MUL_RESULT:%.*]] = extractvalue { i32, i1 } [[MUL]], 0
23+
; RV32-NEXT: [[MUL_OVERFLOW:%.*]] = extractvalue { i32, i1 } [[MUL]], 1
24+
; RV32-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[A:%.*]], i32 [[MUL_RESULT]]
25+
; RV32-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[A]]
26+
; RV32-NEXT: [[TMP2:%.*]] = or i1 [[TMP1]], [[MUL_OVERFLOW]]
27+
; RV32-NEXT: [[MUL1:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 256, i32 624)
28+
; RV32-NEXT: [[MUL_RESULT2:%.*]] = extractvalue { i32, i1 } [[MUL1]], 0
29+
; RV32-NEXT: [[MUL_OVERFLOW3:%.*]] = extractvalue { i32, i1 } [[MUL1]], 1
30+
; RV32-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[B:%.*]], i32 [[MUL_RESULT2]]
31+
; RV32-NEXT: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[B]]
32+
; RV32-NEXT: [[TMP5:%.*]] = or i1 [[TMP4]], [[MUL_OVERFLOW3]]
33+
; RV32-NEXT: [[TMP6:%.*]] = or i1 [[TMP2]], [[TMP5]]
34+
; RV32-NEXT: br i1 [[TMP6]], label [[SCALAR_PH:%.*]], label [[VECTOR_MEMCHECK1:%.*]]
2035
; RV32: vector.memcheck:
21-
; RV32-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[A:%.*]], i32 79880
2236
; RV32-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[TRIGGER:%.*]], i32 39940
23-
; RV32-NEXT: [[SCEVGEP2:%.*]] = getelementptr i8, ptr [[B:%.*]], i32 159752
24-
; RV32-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[A]], [[SCEVGEP1]]
25-
; RV32-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[TRIGGER]], [[SCEVGEP]]
37+
; RV32-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[A]], i32 79880
38+
; RV32-NEXT: [[SCEVGEP2:%.*]] = getelementptr i8, ptr [[B]], i32 159752
39+
; RV32-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[TRIGGER]], [[SCEVGEP]]
40+
; RV32-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[A]], [[SCEVGEP1]]
2641
; RV32-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]]
2742
; RV32-NEXT: [[BOUND03:%.*]] = icmp ult ptr [[A]], [[SCEVGEP2]]
2843
; RV32-NEXT: [[BOUND14:%.*]] = icmp ult ptr [[B]], [[SCEVGEP]]
2944
; RV32-NEXT: [[FOUND_CONFLICT5:%.*]] = and i1 [[BOUND03]], [[BOUND14]]
3045
; RV32-NEXT: [[CONFLICT_RDX:%.*]] = or i1 [[FOUND_CONFLICT]], [[FOUND_CONFLICT5]]
31-
; RV32-NEXT: br i1 [[CONFLICT_RDX]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
46+
; RV32-NEXT: br i1 [[CONFLICT_RDX]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]]
3247
; RV32: vector.ph:
3348
; RV32-NEXT: [[TMP7:%.*]] = call <vscale x 2 x i64> @llvm.stepvector.nxv2i64()
3449
; RV32-NEXT: [[TMP9:%.*]] = mul <vscale x 2 x i64> [[TMP7]], splat (i64 16)
@@ -43,25 +58,26 @@ define void @foo4(ptr nocapture %A, ptr nocapture readonly %B, ptr nocapture rea
4358
; RV32-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <vscale x 2 x i64> poison, i64 [[TMP11]], i64 0
4459
; RV32-NEXT: [[DOTSPLAT:%.*]] = shufflevector <vscale x 2 x i64> [[BROADCAST_SPLATINSERT]], <vscale x 2 x i64> poison, <vscale x 2 x i32> zeroinitializer
4560
; RV32-NEXT: [[TMP13:%.*]] = getelementptr inbounds i32, ptr [[TRIGGER]], <vscale x 2 x i64> [[VEC_IND]]
46-
; RV32-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call <vscale x 2 x i32> @llvm.vp.gather.nxv2i32.nxv2p0(<vscale x 2 x ptr> align 4 [[TMP13]], <vscale x 2 x i1> splat (i1 true), i32 [[TMP10]]), !alias.scope [[META0:![0-9]+]]
61+
; RV32-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call <vscale x 2 x i32> @llvm.vp.gather.nxv2i32.nxv2p0(<vscale x 2 x ptr> align 4 [[TMP13]], <vscale x 2 x i1> splat (i1 true), i32 [[TMP10]]), !alias.scope [[META0:![0-9]+]], !noalias [[META3:![0-9]+]]
4762
; RV32-NEXT: [[TMP14:%.*]] = icmp slt <vscale x 2 x i32> [[WIDE_MASKED_GATHER]], splat (i32 100)
4863
; RV32-NEXT: [[TMP15:%.*]] = shl nuw nsw <vscale x 2 x i64> [[VEC_IND]], splat (i64 1)
4964
; RV32-NEXT: [[TMP16:%.*]] = getelementptr inbounds double, ptr [[B]], <vscale x 2 x i64> [[TMP15]]
50-
; RV32-NEXT: [[WIDE_MASKED_GATHER6:%.*]] = call <vscale x 2 x double> @llvm.vp.gather.nxv2f64.nxv2p0(<vscale x 2 x ptr> align 8 [[TMP16]], <vscale x 2 x i1> [[TMP14]], i32 [[TMP10]]), !alias.scope [[META3:![0-9]+]]
65+
; RV32-NEXT: [[WIDE_MASKED_GATHER6:%.*]] = call <vscale x 2 x double> @llvm.vp.gather.nxv2f64.nxv2p0(<vscale x 2 x ptr> align 8 [[TMP16]], <vscale x 2 x i1> [[TMP14]], i32 [[TMP10]]), !alias.scope [[META5:![0-9]+]]
5166
; RV32-NEXT: [[TMP17:%.*]] = sitofp <vscale x 2 x i32> [[WIDE_MASKED_GATHER]] to <vscale x 2 x double>
5267
; RV32-NEXT: [[TMP18:%.*]] = fadd <vscale x 2 x double> [[WIDE_MASKED_GATHER6]], [[TMP17]]
5368
; RV32-NEXT: [[TMP19:%.*]] = getelementptr inbounds double, ptr [[A]], <vscale x 2 x i64> [[VEC_IND]]
54-
; RV32-NEXT: call void @llvm.vp.scatter.nxv2f64.nxv2p0(<vscale x 2 x double> [[TMP18]], <vscale x 2 x ptr> align 8 [[TMP19]], <vscale x 2 x i1> [[TMP14]], i32 [[TMP10]]), !alias.scope [[META5:![0-9]+]], !noalias [[META7:![0-9]+]]
69+
; RV32-NEXT: call void @llvm.vp.scatter.nxv2f64.nxv2p0(<vscale x 2 x double> [[TMP18]], <vscale x 2 x ptr> align 8 [[TMP19]], <vscale x 2 x i1> [[TMP14]], i32 [[TMP10]]), !alias.scope [[META3]], !noalias [[META5]]
5570
; RV32-NEXT: [[AVL_NEXT]] = sub nuw i64 [[AVL]], [[TMP8]]
5671
; RV32-NEXT: [[VEC_IND_NEXT]] = add <vscale x 2 x i64> [[VEC_IND]], [[DOTSPLAT]]
5772
; RV32-NEXT: [[TMP24:%.*]] = icmp eq i64 [[AVL_NEXT]], 0
58-
; RV32-NEXT: br i1 [[TMP24]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]]
73+
; RV32-NEXT: br i1 [[TMP24]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP7:![0-9]+]]
5974
; RV32: middle.block:
6075
; RV32-NEXT: br label [[FOR_END:%.*]]
6176
; RV32: scalar.ph:
77+
; RV32-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ 0, [[VECTOR_MEMCHECK]] ], [ 0, [[VECTOR_MEMCHECK1]] ]
6278
; RV32-NEXT: br label [[FOR_BODY:%.*]]
6379
; RV32: for.body:
64-
; RV32-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[SCALAR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ]
80+
; RV32-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ]
6581
; RV32-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TRIGGER]], i64 [[INDVARS_IV]]
6682
; RV32-NEXT: [[TMP21:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
6783
; RV32-NEXT: [[CMP1:%.*]] = icmp slt i32 [[TMP21]], 100
@@ -78,7 +94,7 @@ define void @foo4(ptr nocapture %A, ptr nocapture readonly %B, ptr nocapture rea
7894
; RV32: for.inc:
7995
; RV32-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 16
8096
; RV32-NEXT: [[CMP:%.*]] = icmp ult i64 [[INDVARS_IV_NEXT]], 10000
81-
; RV32-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]], !llvm.loop [[LOOP12:![0-9]+]]
97+
; RV32-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]], !llvm.loop [[LOOP10:![0-9]+]]
8298
; RV32: for.end:
8399
; RV32-NEXT: ret void
84100
;
@@ -146,7 +162,7 @@ define void @foo4(ptr nocapture %A, ptr nocapture readonly %B, ptr nocapture rea
146162
; RV64: for.inc:
147163
; RV64-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 16
148164
; RV64-NEXT: [[CMP:%.*]] = icmp ult i64 [[INDVARS_IV_NEXT]], 10000
149-
; RV64-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]], !llvm.loop [[LOOP12:![0-9]+]]
165+
; RV64-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END]], !llvm.loop [[LOOP11:![0-9]+]]
150166
; RV64: for.end:
151167
; RV64-NEXT: ret void
152168
;

0 commit comments

Comments
 (0)