[llvm] [ConstraintElim] Handle single And/Or as exit condition in loop. (PR #221228)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 00:50:51 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/221228
>From d72d0d6773e07be5fb1208b07f54144738234c82 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 4 Sep 2026 14:15:46 +0100
Subject: [PATCH 1/2] [ConstraintElim] Add tests for loops with and/or exit
conditions.
---
.../induction-exit-condition-or-and.ll | 474 ++++++++++++++++++
1 file changed, 474 insertions(+)
create mode 100644 llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll b/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
new file mode 100644
index 0000000000000..5c4679e606894
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
@@ -0,0 +1,474 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @use(i1)
+
+define void @stride2_or_latch(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_or_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = or i1 [[X]], [[EC]]
+; CHECK-NEXT: br i1 [[COND]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ %cond = or i1 %x, %ec
+ br i1 %cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @stride2_or_latch_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_or_latch_operands_swapped(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = or i1 [[EC]], [[X]]
+; CHECK-NEXT: br i1 [[COND]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ %cond = or i1 %ec, %x
+ br i1 %cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @neg_or_on_continue_edge(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_or_on_continue_edge(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = or i1 [[X]], [[EC]]
+; CHECK-NEXT: br i1 [[COND]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp ne i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ %cond = or i1 %x, %ec
+ br i1 %cond, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @neg_or_on_continue_edge_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_or_on_continue_edge_operands_swapped(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = or i1 [[EC]], [[X]]
+; CHECK-NEXT: br i1 [[COND]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp ne i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ %cond = or i1 %ec, %x
+ br i1 %cond, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @sel_or_peel(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @sel_or_peel(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[X]], i1 true, i1 [[EC]]
+; CHECK-NEXT: br i1 [[COND]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ %cond = select i1 %x, i1 true, i1 %ec
+ br i1 %cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @stride2_and_latch(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_and_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = and i1 [[X]], [[EC]]
+; CHECK-NEXT: br i1 [[COND]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ %cond = and i1 %x, %ec
+ br i1 %cond, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @stride2_and_latch_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_and_latch_operands_swapped(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = and i1 [[EC]], [[X]]
+; CHECK-NEXT: br i1 [[COND]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ %cond = and i1 %ec, %x
+ br i1 %cond, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @neg_and_on_exit_edge(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_and_on_exit_edge(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = and i1 [[X]], [[EC]]
+; CHECK-NEXT: br i1 [[COND]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ %cond = and i1 %x, %ec
+ br i1 %cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @neg_and_on_exit_edge_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_and_on_exit_edge_operands_swapped(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = and i1 [[EC]], [[X]]
+; CHECK-NEXT: br i1 [[COND]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ %cond = and i1 %ec, %x
+ br i1 %cond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @sel_and_peel(i64 %n, i1 %x, i1 %header.ec) {
+; CHECK-LABEL: define void @sel_and_peel(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[X:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE_C:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE_C]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
+; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
+; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[X]], i1 [[EC]], i1 false
+; CHECK-NEXT: br i1 [[COND]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre.c = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre.c)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ %n.minus.1 = sub nuw i64 %n, 1
+ %c.tight = icmp ult i64 %iv, %n.minus.1
+ call void @use(i1 %c.tight)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ %cond = select i1 %x, i1 %ec, i1 false
+ br i1 %cond, label %loop.header, label %exit
+
+exit:
+ ret void
+}
>From 97e7f03cc1f33dee071add4add749f30576fa067 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 31 Aug 2026 16:44:06 +0100
Subject: [PATCH 2/2] [ConstraintElim] Handle single And/Or as exit condition
in loop.
In some cases, exits conditions in the loop are And/Or of and IV-based
condition and another condition. If the loop continues only if all
conditions are true (AND + true successor is the header, OR + false
successor is the header), we can still use the IV based condition to
bound the IV.
For now, just peels a single AND/OR.
This enables folds in a few additional cases:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1193.
---
.../Scalar/ConstraintElimination.cpp | 26 ++++++++++++++++---
.../and-implied-by-operands.ll | 3 +--
.../induction-exit-condition-or-and.ll | 18 +++++--------
3 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index c4bc584028e83..26b9ad4073dfe 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1086,9 +1086,23 @@ void State::addInfoForInductions(BasicBlock &BB) {
auto IndValue =
m_Value(A, m_CombineOr(m_Phi(PN), m_c_Add(m_Phi(PN), m_APInt(IncStep))));
- if (!match(BB.getTerminator(),
- m_Br(m_c_ICmp(Pred, IndValue, m_Value(B)), m_Value(), m_Value())))
+ auto *Br = dyn_cast<CondBrInst>(BB.getTerminator());
+ if (!Br)
return;
+
+ auto CountingCmp = m_c_ICmp(Pred, IndValue, m_Value(B));
+ std::optional<bool> PeeledOnEdge;
+ if (!match(Br->getCondition(), CountingCmp)) {
+ // Look through AND/OR, and remember which edge requires all operands to be
+ // true.
+ if (match(Br->getCondition(), m_c_LogicalAnd(CountingCmp, m_Value())))
+ PeeledOnEdge = true;
+ else if (match(Br->getCondition(), m_c_LogicalOr(CountingCmp, m_Value())))
+ PeeledOnEdge = false;
+ else
+ return;
+ }
+
if (PN->getParent() != Header || PN->getNumIncomingValues() != 2 ||
!SE.isSCEVable(PN->getType()))
return;
@@ -1106,8 +1120,12 @@ void State::addInfoForInductions(BasicBlock &BB) {
Pred == CmpInst::ICMP_NE || ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred);
CmpInst::Predicate ContinuePred =
ContinueOnTrue ? Pred.dropSameSign() : CmpInst::getInversePredicate(Pred);
- BasicBlock *InLoopSucc = cast<CondBrInst>(BB.getTerminator())
- ->getSuccessor(ContinueOnTrue ? 0 : 1);
+ BasicBlock *InLoopSucc = Br->getSuccessor(ContinueOnTrue ? 0 : 1);
+
+ // The peeled condition only implies the compare on the edge where the
+ // combined condition forces its operands, which must be the in-loop edge.
+ if (PeeledOnEdge && *PeeledOnEdge != ContinueOnTrue)
+ return;
if (!L->contains(InLoopSucc) || !L->isLoopExiting(&BB) || InLoopSucc == &BB)
return;
diff --git a/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll b/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
index 78a91b56a59cf..820010e28cf21 100644
--- a/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
+++ b/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
@@ -321,8 +321,7 @@ define void @test_monotonic_ptr_iv_inc_1_eq_to_uge(ptr %start, i16 %len) {
; CHECK-NEXT: [[AND_0:%.*]] = and i1 [[LEN_NEG]], [[C]]
; CHECK-NEXT: br i1 [[AND_0]], label [[FOR_BODY:%.*]], label [[EXIT:%.*]]
; CHECK: for.body:
-; CHECK-NEXT: [[T_2:%.*]] = icmp ult ptr [[PTR_IV]], [[UPPER]]
-; CHECK-NEXT: [[AND:%.*]] = and i1 true, [[T_2]]
+; CHECK-NEXT: [[AND:%.*]] = and i1 true, true
; CHECK-NEXT: br i1 [[AND]], label [[LOOP_LATCH]], label [[EXIT]]
; CHECK: loop.latch:
; CHECK-NEXT: call void @use(ptr [[PTR_IV]])
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll b/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
index 5c4679e606894..a333b7c7369a2 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-exit-condition-or-and.ll
@@ -12,8 +12,7 @@ define void @stride2_or_latch(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
@@ -59,8 +58,7 @@ define void @stride2_or_latch_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
@@ -200,8 +198,7 @@ define void @sel_or_peel(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
@@ -247,8 +244,7 @@ define void @stride2_and_latch(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
@@ -294,8 +290,7 @@ define void @stride2_and_latch_operands_swapped(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
@@ -435,8 +430,7 @@ define void @sel_and_peel(i64 %n, i1 %x, i1 %header.ec) {
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
-; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[N_MINUS_1:%.*]] = sub nuw i64 [[N]], 1
; CHECK-NEXT: [[C_TIGHT:%.*]] = icmp ult i64 [[IV]], [[N_MINUS_1]]
; CHECK-NEXT: call void @use(i1 [[C_TIGHT]])
More information about the llvm-commits
mailing list