[llvm] [CVP] Add tests for single-user result refinement. (NFC) (PR #211901)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 24 13:04:04 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/211901
Extra tests for https://github.com/llvm/llvm-project/pull/211668.
>From bf655e7f5b3f774088381240b1d8d217bc69f474 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 23 Jul 2026 20:26:11 +0100
Subject: [PATCH] [CVP] Add tests for single-user result refinement. (NFC)
Extra tests for https://github.com/llvm/llvm-project/pull/211668.
---
.../range-single-user.ll | 326 ++++++++++++++++++
1 file changed, 326 insertions(+)
create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/range-single-user.ll
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/range-single-user.ll b/llvm/test/Transforms/CorrelatedValuePropagation/range-single-user.ll
new file mode 100644
index 0000000000000..9cefb707b9369
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/range-single-user.ll
@@ -0,0 +1,326 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=correlated-propagation -S %s | FileCheck %s
+
+
+declare void @use.i16(i16)
+
+define i8 @refine_add_single_cross_block_user(i8 %a) {
+; CHECK-LABEL: define i8 @refine_add_single_cross_block_user(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 93
+; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i8 96
+;
+entry:
+ %c = add i8 %a, 3
+ br label %loop
+
+loop:
+ %cmp = icmp eq i8 %a, 93
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret i8 %c
+}
+
+define void @no_refine_udiv_unsafe(i16 %x, i16 %y, ptr %p) {
+; CHECK-LABEL: define void @no_refine_udiv_unsafe(
+; CHECK-SAME: i16 [[X:%.*]], i16 [[Y:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[D:%.*]] = udiv i16 [[X]], [[Y]]
+; CHECK-NEXT: [[CX:%.*]] = icmp ult i16 [[X]], 256
+; CHECK-NEXT: [[CY:%.*]] = icmp ult i16 [[Y]], 256
+; CHECK-NEXT: [[OK:%.*]] = and i1 [[CX]], [[CY]]
+; CHECK-NEXT: br i1 [[OK]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: store i16 [[D]], ptr [[P]], align 2
+; CHECK-NEXT: ret void
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %d = udiv i16 %x, %y
+ %cx = icmp ult i16 %x, 256
+ %cy = icmp ult i16 %y, 256
+ %ok = and i1 %cx, %cy
+ br i1 %ok, label %then, label %else
+
+then:
+ store i16 %d, ptr %p
+ ret void
+
+else:
+ ret void
+}
+
+define void @no_refine_sdiv_unsafe(i16 %x, i16 %y, ptr %p) {
+; CHECK-LABEL: define void @no_refine_sdiv_unsafe(
+; CHECK-SAME: i16 [[X:%.*]], i16 [[Y:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[D:%.*]] = sdiv i16 [[X]], [[Y]]
+; CHECK-NEXT: [[CX:%.*]] = icmp ult i16 [[X]], 100
+; CHECK-NEXT: [[CY:%.*]] = icmp ult i16 [[Y]], 100
+; CHECK-NEXT: [[OK:%.*]] = and i1 [[CX]], [[CY]]
+; CHECK-NEXT: br i1 [[OK]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: store i16 [[D]], ptr [[P]], align 2
+; CHECK-NEXT: ret void
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %d = sdiv i16 %x, %y
+ %cx = icmp ult i16 %x, 100
+ %cy = icmp ult i16 %y, 100
+ %ok = and i1 %cx, %cy
+ br i1 %ok, label %then, label %else
+
+then:
+ store i16 %d, ptr %p
+ ret void
+
+else:
+ ret void
+}
+
+define i8 @no_refine_multiple_users(i8 %a) {
+; CHECK-LABEL: define i8 @no_refine_multiple_users(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 93
+; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[C_EXT:%.*]] = zext nneg i8 [[C]] to i16
+; CHECK-NEXT: call void @use.i16(i16 [[C_EXT]])
+; CHECK-NEXT: ret i8 96
+;
+entry:
+ %c = add i8 %a, 3
+ br label %loop
+
+loop:
+ %cmp = icmp eq i8 %a, 93
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ %c.ext = zext i8 %c to i16
+ call void @use.i16(i16 %c.ext)
+ ret i8 %c
+}
+
+define i8 @no_refine_phi_user(i8 %a) {
+; CHECK-LABEL: define range(i8 0, 97) i8 @no_refine_phi_user(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 93
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[P:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ 96, %[[THEN]] ]
+; CHECK-NEXT: ret i8 [[P]]
+;
+entry:
+ %c = add i8 %a, 3
+ %cmp = icmp eq i8 %a, 93
+ br i1 %cmp, label %then, label %exit
+
+then:
+ br label %exit
+
+exit:
+ %p = phi i8 [ 0, %entry ], [ %c, %then ]
+ ret i8 %p
+}
+
+define i8 @no_refine_same_block_user(i8 %a) {
+; CHECK-LABEL: define i8 @no_refine_same_block_user(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = add nuw nsw i8 [[A]], 3
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 93
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 96
+;
+entry:
+ %c = add i8 %a, 3
+ %cmp = icmp eq i8 %a, 93
+ call void @llvm.assume(i1 %cmp)
+ ret i8 %c
+}
+
+define i8 @no_refine_droppable_user(i8 %a) {
+; CHECK-LABEL: define i8 @no_refine_droppable_user(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[C]], 96
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 96
+;
+entry:
+ %c = add i8 %a, 3
+ %cmp = icmp eq i8 %c, 96
+ call void @llvm.assume(i1 %cmp)
+ ret i8 %c
+}
+
+; Make sure drop-able uses triggering UB on poison are not ignored when
+; refining flags in use context.
+define i32 @no_refine_samesign_extra_droppable_user(i8 %x, i8 %y, i1 %g) {
+; CHECK-LABEL: define range(i32 0, 2) i32 @no_refine_samesign_extra_droppable_user(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i1 [[G:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[G]], label %[[THEN:.*]], label %[[ASSUME_BB:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[PX:%.*]] = icmp sgt i8 [[X]], -1
+; CHECK-NEXT: call void @llvm.assume(i1 [[PX]])
+; CHECK-NEXT: [[PY:%.*]] = icmp sgt i8 [[Y]], -1
+; CHECK-NEXT: call void @llvm.assume(i1 [[PY]])
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[C]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+; CHECK: [[ASSUME_BB]]:
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp slt i8 %x, %y
+ br i1 %g, label %then, label %assume_bb
+
+then:
+ %px = icmp sgt i8 %x, -1
+ call void @llvm.assume(i1 %px)
+ %py = icmp sgt i8 %y, -1
+ call void @llvm.assume(i1 %py)
+ %r = zext i1 %c to i32
+ ret i32 %r
+
+assume_bb:
+ call void @llvm.assume(i1 %c)
+ ret i32 0
+}
+
+; Same as above for a poison-generating cast flag.
+define void @no_refine_nneg_extra_droppable_bundle(i8 %a, i1 %g) {
+; CHECK-LABEL: define void @no_refine_nneg_extra_droppable_bundle(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[G:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[Z:%.*]] = zext i8 [[A]] to i16
+; CHECK-NEXT: br i1 [[G]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[POS:%.*]] = icmp sgt i8 [[A]], -1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT: call void @use.i16(i16 [[Z]])
+; CHECK-NEXT: ret void
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "noundef"(i16 [[Z]]) ]
+; CHECK-NEXT: ret void
+;
+entry:
+ %z = zext i8 %a to i16
+ br i1 %g, label %then, label %else
+
+then:
+ %pos = icmp sgt i8 %a, -1
+ call void @llvm.assume(i1 %pos)
+ call void @use.i16(i16 %z)
+ ret void
+
+else:
+ call void @llvm.assume(i1 true) ["noundef"(i16 %z)]
+ ret void
+}
+
+; %n is known to be non-negative, so umax(%n, 1) == %n and the exit condition
+; icmp eq 0, %umax is known false. The only user of %umax is the the compare in
+; the loop.
+define i64 @test_umax_single_user_across_loop(i64 %n) {
+; CHECK-LABEL: define i64 @test_umax_single_user_across_loop(
+; CHECK-SAME: i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[N]], 0
+; CHECK-NEXT: br i1 [[Z]], label %[[EXIT:.*]], label %[[PH:.*]]
+; CHECK: [[PH]]:
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: call void @foo()
+; CHECK-NEXT: br label %[[LOOP_LATCH:.*]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 0, [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i64 [[N]]
+;
+entry:
+ %z = icmp eq i64 %n, 0
+ br i1 %z, label %exit, label %ph
+
+ph:
+ %umax = call i64 @llvm.umax.i64(i64 %n, i64 1)
+ br label %loop.header
+
+loop.header:
+ call void @foo()
+ br label %loop.latch
+
+loop.latch:
+ %ec = icmp eq i64 0, %umax
+ br i1 %ec, label %exit, label %loop.header
+
+exit:
+ %r = phi i64 [ 0, %entry ], [ %n, %loop.latch ]
+ ret i64 %r
+}
+
+; %n is known non-negative on the path into the loop (%n <= 100), with a
+; single user after the loop.
+define i64 @zext_nneg_single_user_across_loop(i32 %n) {
+; CHECK-LABEL: define i64 @zext_nneg_single_user_across_loop(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[BIG:%.*]] = icmp ugt i32 [[N]], 100
+; CHECK-NEXT: br i1 [[BIG]], label %[[EXIT:.*]], label %[[PH:.*]]
+; CHECK: [[PH]]:
+; CHECK-NEXT: [[Z:%.*]] = zext nneg i32 [[N]] to i64
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: call void @foo()
+; CHECK-NEXT: br label %[[LOOP_LATCH:.*]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 0, [[Z]]
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i64 0
+;
+entry:
+ %big = icmp ugt i32 %n, 100
+ br i1 %big, label %exit, label %ph
+
+ph:
+ %z = zext i32 %n to i64
+ br label %loop.header
+
+loop.header:
+ call void @foo()
+ br label %loop.latch
+
+loop.latch:
+ %ec = icmp eq i64 0, %z
+ br i1 %ec, label %exit, label %loop.header
+
+exit:
+ %r = phi i64 [ 0, %entry ], [ %z, %loop.latch ]
+ ret i64 %r
+}
+
+declare void @foo(i32)
More information about the llvm-commits
mailing list