https://github.com/kasuga-fj created https://github.com/llvm/llvm-project/pull/202726
None >From 5a62b2a1f12ed32029d6d4a7f3d41909f85f4ef6 Mon Sep 17 00:00:00 2001 From: Ryotaro Kasuga <[email protected]> Date: Wed, 10 Jun 2026 02:51:15 +0900 Subject: [PATCH] [LoopInterchange] Bail out if inner latch branch cond is not CmpInst --- .../lib/Transforms/Scalar/LoopInterchange.cpp | 98 ++++++++++--------- .../complex-inner-latch-condition.ll | 31 ++---- 2 files changed, 58 insertions(+), 71 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp index cb1327b75260f..f0cf9d6d4724f 100644 --- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp +++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp @@ -923,60 +923,62 @@ bool LoopInterchangeLegality::isLoopStructureUnderstood() { dyn_cast<CondBrInst>(InnerLoopLatch->getTerminator()); if (!InnerLoopLatchBI) return false; - if (CmpInst *InnerLoopCmp = - dyn_cast<CmpInst>(InnerLoopLatchBI->getCondition())) { - Value *Op0 = InnerLoopCmp->getOperand(0); - Value *Op1 = InnerLoopCmp->getOperand(1); - - // LHS and RHS of the inner loop exit condition, e.g., - // in "for(int j=0;j<i;j++)", LHS is j and RHS is i. - Value *Left = nullptr; - Value *Right = nullptr; - - // Check if V only involves inner loop induction variable. - // Return true if V is InnerInduction, or a cast from - // InnerInduction, or a binary operator that involves - // InnerInduction and a constant. - std::function<bool(Value *)> IsPathToInnerIndVar; - IsPathToInnerIndVar = [this, &IsPathToInnerIndVar](const Value *V) -> bool { - if (llvm::is_contained(InnerLoopInductions, V)) - return true; - if (isa<Constant>(V)) - return true; - const Instruction *I = dyn_cast<Instruction>(V); - if (!I) - return false; - if (isa<CastInst>(I)) - return IsPathToInnerIndVar(I->getOperand(0)); - if (isa<BinaryOperator>(I)) - return IsPathToInnerIndVar(I->getOperand(0)) && - IsPathToInnerIndVar(I->getOperand(1)); - return false; - }; - - // In case of multiple inner loop indvars, it is okay if LHS and RHS - // are both inner indvar related variables. - if (IsPathToInnerIndVar(Op0) && IsPathToInnerIndVar(Op1)) - return true; - // Otherwise we check if the cmp instruction compares an inner indvar - // related variable (Left) with a outer loop invariant (Right). - if (IsPathToInnerIndVar(Op0) && !isa<Constant>(Op0)) { - Left = Op0; - Right = Op1; - } else if (IsPathToInnerIndVar(Op1) && !isa<Constant>(Op1)) { - Left = Op1; - Right = Op0; - } + CmpInst *InnerLoopCmp = dyn_cast<CmpInst>(InnerLoopLatchBI->getCondition()); + if (!InnerLoopCmp) + return false; - if (Left == nullptr) + Value *Op0 = InnerLoopCmp->getOperand(0); + Value *Op1 = InnerLoopCmp->getOperand(1); + + // LHS and RHS of the inner loop exit condition, e.g., + // in "for(int j=0;j<i;j++)", LHS is j and RHS is i. + Value *Left = nullptr; + Value *Right = nullptr; + + // Check if V only involves inner loop induction variable. + // Return true if V is InnerInduction, or a cast from + // InnerInduction, or a binary operator that involves + // InnerInduction and a constant. + std::function<bool(Value *)> IsPathToInnerIndVar; + IsPathToInnerIndVar = [this, &IsPathToInnerIndVar](const Value *V) -> bool { + if (llvm::is_contained(InnerLoopInductions, V)) + return true; + if (isa<Constant>(V)) + return true; + const Instruction *I = dyn_cast<Instruction>(V); + if (!I) return false; + if (isa<CastInst>(I)) + return IsPathToInnerIndVar(I->getOperand(0)); + if (isa<BinaryOperator>(I)) + return IsPathToInnerIndVar(I->getOperand(0)) && + IsPathToInnerIndVar(I->getOperand(1)); + return false; + }; - const SCEV *S = SE->getSCEV(Right); - if (!SE->isLoopInvariant(S, OuterLoop)) - return false; + // In case of multiple inner loop indvars, it is okay if LHS and RHS + // are both inner indvar related variables. + if (IsPathToInnerIndVar(Op0) && IsPathToInnerIndVar(Op1)) + return true; + + // Otherwise we check if the cmp instruction compares an inner indvar + // related variable (Left) with a outer loop invariant (Right). + if (IsPathToInnerIndVar(Op0) && !isa<Constant>(Op0)) { + Left = Op0; + Right = Op1; + } else if (IsPathToInnerIndVar(Op1) && !isa<Constant>(Op1)) { + Left = Op1; + Right = Op0; } + if (Left == nullptr) + return false; + + const SCEV *S = SE->getSCEV(Right); + if (!SE->isLoopInvariant(S, OuterLoop)) + return false; + return true; } diff --git a/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll b/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll index 1527893167db5..a4d74fa5971cd 100644 --- a/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll +++ b/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll @@ -8,44 +8,29 @@ ; We currently doesn't support interchanging triangular loops, so the above ; loops must not be interchanged. ; -; FIXME: Currently loop-interchange is applied. -; define void @complex_inner_latch_cond(ptr %A) { ; CHECK-LABEL: define void @complex_inner_latch_cond( ; CHECK-SAME: ptr [[A:%.*]]) { -; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: br label %[[FOR_J_PREHEADER:.*]] -; CHECK: [[FOR_I_HEADER_PREHEADER:.*]]: -; CHECK-NEXT: br label %[[FOR_I_HEADER:.*]] -; CHECK: [[FOR_I_HEADER]]: -; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ], [ 0, %[[FOR_I_HEADER_PREHEADER]] ] -; CHECK-NEXT: br label %[[FOR_J_SPLIT1:.*]] -; CHECK: [[FOR_J_PREHEADER]]: +; CHECK-NEXT: [[FOR_J_PREHEADER:.*]]: ; CHECK-NEXT: br label %[[FOR_J:.*]] ; CHECK: [[FOR_J]]: -; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[FOR_J_SPLIT:.*]] ], [ 0, %[[FOR_J_PREHEADER]] ] -; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER]] -; CHECK: [[FOR_J_SPLIT1]]: +; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[FOR_J_PREHEADER]] ], [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ] +; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER:.*]] +; CHECK: [[FOR_I_HEADER_PREHEADER]]: +; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[FOR_J]] ], [ [[J_NEXT:%.*]], %[[FOR_I_HEADER_PREHEADER]] ] ; CHECK-NEXT: [[GEP:%.*]] = getelementptr [5 x i8], ptr [[A]], i64 [[J]], i64 [[I]] ; CHECK-NEXT: [[OLD:%.*]] = load i8, ptr [[GEP]], align 1 ; CHECK-NEXT: [[NEW:%.*]] = add i8 [[OLD]], 1 ; CHECK-NEXT: store i8 [[NEW]], ptr [[GEP]], align 1 -; CHECK-NEXT: [[J_NEXT:%.*]] = add i64 [[J]], 1 +; CHECK-NEXT: [[J_NEXT]] = add i64 [[J]], 1 ; CHECK-NEXT: [[C0:%.*]] = icmp slt i64 [[J_NEXT]], [[I]] ; CHECK-NEXT: [[C1:%.*]] = icmp slt i64 [[J_NEXT]], 100 ; CHECK-NEXT: [[EC_J_NOT:%.*]] = and i1 [[C0]], [[C1]] -; CHECK-NEXT: br label %[[FOR_I_LATCH]] -; CHECK: [[FOR_J_SPLIT]]: -; CHECK-NEXT: [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[FOR_I_LATCH]] ] -; CHECK-NEXT: [[TMP0]] = add i64 [[J]], 1 -; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i64 [[TMP0]], 100 -; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i64 [[TMP0]], [[I_LCSSA]] -; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]] -; CHECK-NEXT: br i1 [[TMP3]], label %[[FOR_J]], label %[[EXIT:.*]] +; CHECK-NEXT: br i1 [[EC_J_NOT]], label %[[FOR_I_HEADER_PREHEADER]], label %[[FOR_I_LATCH]] ; CHECK: [[FOR_I_LATCH]]: ; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1 ; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 5 -; CHECK-NEXT: br i1 [[EC_I]], label %[[FOR_J_SPLIT]], label %[[FOR_I_HEADER]] +; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[FOR_J]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
