[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