[llvm] [InstCombine] Fold binop into multi-use select when one select arm and the other operand are constant (PR #196583)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Fri May 8 23:38:58 PDT 2026
================
@@ -0,0 +1,178 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define noundef i32 @rust_155985(i32 noundef %n, i1 noundef zeroext %b) {
+; CHECK-LABEL: @rust_155985(
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[EQ:%.*]] = icmp eq i32 [[N:%.*]], 0
+; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[EQ]], i32 1, i32 2
+; CHECK-NEXT: [[FINAL:%.*]] = select i1 [[B:%.*]], i32 0, i32 [[TMP0]]
+; CHECK-NEXT: ret i32 [[FINAL]]
+;
+start:
+ %eq = icmp eq i32 %n, 0
+ ; Err(X::_1) -> 0x00000001_00000001, Err(X::_2) -> 0x00000002_00000001
+ %inner = select i1 %eq, i64 4294967297, i64 8589934593
+ ; Ok(()) -> 0
+ %outer = select i1 %b, i64 0, i64 %inner
+ %tag = trunc i64 %outer to i1
+ %shift = lshr i64 %outer, 32
+ %payload = trunc i64 %shift to i32
+ %final = select i1 %tag, i32 %payload, i32 0
+ ret i32 %final
+}
+
+define i32 @lshr_through_multi_use_nested_select(i1 %c1, i1 %c2, ptr %sink) {
+; CHECK-LABEL: @lshr_through_multi_use_nested_select(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 4294967297, i64 8589934593
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 0, i64 [[INNER]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[C1]], i32 1, i32 2
+; CHECK-NEXT: [[T:%.*]] = select i1 [[C2]], i32 0, i32 [[TMP1]]
+; CHECK-NEXT: ret i32 [[T]]
+;
+ %inner = select i1 %c1, i64 4294967297, i64 8589934593
+ %outer = select i1 %c2, i64 0, i64 %inner
+ store i64 %outer, ptr %sink
+ %s = lshr i64 %outer, 32
+ %t = trunc i64 %s to i32
+ ret i32 %t
+}
+
+define i64 @and_through_multi_use_nested_select(i1 %c1, i1 %c2, ptr %sink) {
+; CHECK-LABEL: @and_through_multi_use_nested_select(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 4294967297, i64 8589934595
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 0, i64 [[INNER]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = and i64 [[OUTER]], 3
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %inner = select i1 %c1, i64 4294967297, i64 8589934595
+ %outer = select i1 %c2, i64 0, i64 %inner
+ store i64 %outer, ptr %sink
+ %m = and i64 %outer, 4294967295
+ ret i64 %m
+}
+
+define i64 @or_through_multi_use_nested_select(i1 %c1, i1 %c2, ptr %sink) {
+; CHECK-LABEL: @or_through_multi_use_nested_select(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 10, i64 20
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 0, i64 [[INNER]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = or i64 [[OUTER]], 3
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %inner = select i1 %c1, i64 10, i64 20
+ %outer = select i1 %c2, i64 0, i64 %inner
+ store i64 %outer, ptr %sink
+ %m = or i64 %outer, 3
+ ret i64 %m
+}
+
+define i64 @add_through_multi_use_nested_select(i1 %c1, i1 %c2, ptr %sink) {
+; CHECK-LABEL: @add_through_multi_use_nested_select(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 100, i64 200
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 0, i64 [[INNER]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = add nuw nsw i64 [[OUTER]], 42
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %inner = select i1 %c1, i64 100, i64 200
+ %outer = select i1 %c2, i64 0, i64 %inner
+ store i64 %outer, ptr %sink
+ %m = add i64 %outer, 42
+ ret i64 %m
+}
+
+define i32 @lshr_const_on_true_arm(i1 %c1, i1 %c2, ptr %sink) {
+; CHECK-LABEL: @lshr_const_on_true_arm(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 4294967297, i64 8589934593
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 [[INNER]], i64 0
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[C1]], i32 1, i32 2
+; CHECK-NEXT: [[T:%.*]] = select i1 [[C2]], i32 [[TMP1]], i32 0
+; CHECK-NEXT: ret i32 [[T]]
+;
+ %inner = select i1 %c1, i64 4294967297, i64 8589934593
+ %outer = select i1 %c2, i64 %inner, i64 0
+ store i64 %outer, ptr %sink
+ %s = lshr i64 %outer, 32
+ %t = trunc i64 %s to i32
+ ret i32 %t
+}
+
+define i64 @binop_both_arms_constant_multi_use(i1 %c, ptr %sink) {
+; CHECK-LABEL: @binop_both_arms_constant_multi_use(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i64 100, i64 200
+; CHECK-NEXT: store i64 [[SEL]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = select i1 [[C]], i64 105, i64 205
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %sel = select i1 %c, i64 100, i64 200
+ store i64 %sel, ptr %sink
+ %m = add i64 %sel, 5
+ ret i64 %m
+}
+
+define i64 @nested3_select(i1 %c1, i1 %c2, i1 %c3, ptr %sink) {
+; CHECK-LABEL: @nested3_select(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i64 10, i64 20
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[C3:%.*]], i1 true, i1 [[C2:%.*]]
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[S1]]
+; CHECK-NEXT: store i64 [[S3]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: ret i64 [[S3]]
+;
+ %s1 = select i1 %c1, i64 10, i64 20
+ %s2 = select i1 %c2, i64 0, i64 %s1
+ %s3 = select i1 %c3, i64 0, i64 %s2
+ store i64 %s3, ptr %sink
+ %m = and i64 %s3, 255
+ ret i64 %m
+}
+
+; Negative: binop other operand is not a constant
+define i64 @neg_binop_other_operand_not_const(i1 %c1, i1 %c2, i64 %x, ptr %sink) {
+; CHECK-LABEL: @neg_binop_other_operand_not_const(
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[C1:%.*]], i64 100, i64 200
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C2:%.*]], i64 0, i64 [[INNER]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = add i64 [[OUTER]], [[X:%.*]]
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %inner = select i1 %c1, i64 100, i64 200
+ %outer = select i1 %c2, i64 0, i64 %inner
+ store i64 %outer, ptr %sink
+ %m = add i64 %outer, %x
+ ret i64 %m
+}
+
+; Negative: neither arm of the multi-use select is a constant
+define i64 @neg_no_const_arm(i1 %c, i64 %x, i64 %y, ptr %sink) {
+; CHECK-LABEL: @neg_no_const_arm(
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i64 [[X:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT: store i64 [[SEL]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = add i64 [[SEL]], 42
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %sel = select i1 %c, i64 %x, i64 %y
+ store i64 %sel, ptr %sink
+ %m = add i64 %sel, 42
+ ret i64 %m
+}
+
+; Negative: variable arm of outer select is not a const-select
+define i64 @neg_variable_arm_not_select(i1 %c, i64 %x, ptr %sink) {
+; CHECK-LABEL: @neg_variable_arm_not_select(
+; CHECK-NEXT: [[OUTER:%.*]] = select i1 [[C:%.*]], i64 0, i64 [[X:%.*]]
+; CHECK-NEXT: store i64 [[OUTER]], ptr [[SINK:%.*]], align 8
+; CHECK-NEXT: [[M:%.*]] = add i64 [[OUTER]], 42
+; CHECK-NEXT: ret i64 [[M]]
+;
+ %outer = select i1 %c, i64 0, i64 %x
+ store i64 %outer, ptr %sink
+ %m = add i64 %outer, 42
+ ret i64 %m
+}
----------------
arsenm wrote:
Also test vector cases
https://github.com/llvm/llvm-project/pull/196583
More information about the llvm-commits
mailing list