[llvm] [InstCombine] Fold umax(nuw_mul(x, C0), x + 1) into (x == 0 ? 1 : nuw_mul(x, C0)) (PR #123468)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jan 18 09:30:08 PST 2025
================
@@ -0,0 +1,300 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; When C0 is neither 0 nor 1:
+; umax(nuw_mul(x, C0), x + 1) is optimized to:
+; x == 0 ? 1 : nuw_mul(x, C0)
+; When C0 is not 0:
+; umax(nuw_shl(x, C0), x + 1) is optimized to:
+; x == 0 ? 1 : nuw_shl(x, C0)
+
+; Positive Test Cases for `shl`
+
+define i64 @test_shl_by_2(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_by_2(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 2
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP3]], i64 1, i64 [[TMP2]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_shl_by_5(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_by_5(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 5
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP3]], i64 1, i64 [[TMP2]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 5
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+; Commuted Test Cases for `shl`
+
+define i64 @test_shl_umax_commuted(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_umax_commuted(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP2]], i64 1, i64 [[SHL]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %x1, i64 %shl)
+ ret i64 %max
+}
+
+; Negative Test Cases for `shl`
+
+define i64 @test_shl_by_zero(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_by_zero(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 0
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_shl_add_by_2(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_add_by_2(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 2
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 2
+ %shl = shl nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_shl_without_nuw(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_without_nuw(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], 2
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+; Multi-use Test Cases for `shl`
+declare void @use(i64)
+
+define i64 @test_shl_multi_use_add(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_multi_use_add(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: call void @use(i64 [[X1]])
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 3
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ call void @use(i64 %x1)
+ %shl = shl nuw i64 %x, 3
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_shl_multi_use_shl(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_multi_use_shl(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
+; CHECK-NEXT: call void @use(i64 [[SHL]])
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 2
+ call void @use(i64 %shl)
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_shl_multi_use_max(i64 %x) {
+; CHECK-LABEL: define i64 @test_shl_multi_use_max(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 3
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP3]], i64 1, i64 [[TMP2]]
+; CHECK-NEXT: call void @use(i64 [[MAX]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %shl = shl nuw i64 %x, 3
+ %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
+ call void @use(i64 %max)
+ ret i64 %max
+}
+
+; Positive Test Cases for `mul`
+
+define i64 @test_mul_by_2(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_by_2(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP3]], i64 1, i64 [[TMP2]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_by_5(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_by_5(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 5
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP2]], i64 1, i64 [[MUL]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 5
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+; Commuted Test Cases for `mul`
+
+define i64 @test_mul_max_commuted(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_max_commuted(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP2]], i64 1, i64 [[MUL]]
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %x1, i64 %mul)
+ ret i64 %max
+}
+
+; Negative Test Cases for `mul`
+
+define i64 @test_mul_by_zero(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_by_zero(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: ret i64 [[X1]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 0
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_by_1(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_by_1(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 1
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_add_by_2(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_add_by_2(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 2
+; CHECK-NEXT: [[MUL:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 2
+ %mul = mul nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_without_nuw(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_without_nuw(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[MUL:%.*]] = shl i64 [[X]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+; Multi-use Test Cases for `mul`
+
+define i64 @test_mul_multi_use_add(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_multi_use_add(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: call void @use(i64 [[X1]])
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ call void @use(i64 %x1)
+ %mul = mul nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_multi_use_mul(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_multi_use_mul(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
+; CHECK-NEXT: [[MUL:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: call void @use(i64 [[MUL]])
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 2
+ call void @use(i64 %mul)
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ ret i64 %max
+}
+
+define i64 @test_mul_multi_use_max(i64 %x) {
+; CHECK-LABEL: define i64 @test_mul_multi_use_max(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[X]], 0
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP3]], i64 1, i64 [[TMP2]]
+; CHECK-NEXT: call void @use(i64 [[MAX]])
+; CHECK-NEXT: ret i64 [[MAX]]
+;
+ %x1 = add i64 %x, 1
+ %mul = mul nuw i64 %x, 2
+ %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
+ call void @use(i64 %max)
+ ret i64 %max
----------------
goldsteinn wrote:
The `mul` tests need to have non-power of two multipliers, otherwise they are actually going through the `shl` pass.
https://github.com/llvm/llvm-project/pull/123468
More information about the llvm-commits
mailing list