[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