From 5a8f63570e7a0bfd8ad32c9c117f6c6e27634002 Mon Sep 17 00:00:00 2001 From: Alan Zhao Date: Mon, 20 Oct 2025 16:53:41 -0700 Subject: [PATCH 1/2] [InstCombine][profcheck] Preserve profile with negated select conditionals If a select instruction is replaced with one whose conditional is the negation of the original, then the replacement's branch weights are the reverse of the original's. Tracking issue: #147390 --- .../InstCombine/InstCombineSelect.cpp | 26 +++++++++++++----- .../Transforms/InstCombine/select-and-or.ll | 27 ++++++++++++++----- .../InstCombine/select-safe-transforms.ll | 13 ++++----- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 975498f6d233c..286b22211725b 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -3455,27 +3455,41 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { // select a, false, b -> select !a, b, false if (match(TrueVal, m_Specific(Zero))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); - return SelectInst::Create(NotCond, FalseVal, Zero); + SelectInst *NewSI = + SelectInst::Create(NotCond, FalseVal, Zero, "", nullptr, &SI); + NewSI->swapProfMetadata(); + return NewSI; } // select a, b, true -> select !a, true, b if (match(FalseVal, m_Specific(One))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); - return SelectInst::Create(NotCond, One, TrueVal); + SelectInst *NewSI = + SelectInst::Create(NotCond, One, TrueVal, "", nullptr, &SI); + NewSI->swapProfMetadata(); + return NewSI; } // DeMorgan in select form: !a && !b --> !(a || b) // select !a, !b, false --> not (select a, true, b) if (match(&SI, m_LogicalAnd(m_Not(m_Value(A)), m_Not(m_Value(B)))) && (CondVal->hasOneUse() || TrueVal->hasOneUse()) && - !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) - return BinaryOperator::CreateNot(Builder.CreateSelect(A, One, B)); + !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) { + SelectInst *NewSI = + cast(Builder.CreateSelect(A, One, B, "", &SI)); + NewSI->swapProfMetadata(); + return BinaryOperator::CreateNot(NewSI); + } // DeMorgan in select form: !a || !b --> !(a && b) // select !a, true, !b --> not (select a, b, false) if (match(&SI, m_LogicalOr(m_Not(m_Value(A)), m_Not(m_Value(B)))) && (CondVal->hasOneUse() || FalseVal->hasOneUse()) && - !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) - return BinaryOperator::CreateNot(Builder.CreateSelect(A, B, Zero)); + !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) { + SelectInst *NewSI = + cast(Builder.CreateSelect(A, B, Zero, "", &SI)); + NewSI->swapProfMetadata(); + return BinaryOperator::CreateNot(NewSI); + } // select (select a, true, b), true, b -> select a, true, b if (match(CondVal, m_Select(m_Value(A), m_One(), m_Value(B))) && diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll index 453ca666a7477..0b8eda43beb18 100644 --- a/llvm/test/Transforms/InstCombine/select-and-or.ll +++ b/llvm/test/Transforms/InstCombine/select-and-or.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals ; RUN: opt -S -passes=instcombine < %s | FileCheck %s declare void @use(i1) @@ -6,6 +6,10 @@ declare i1 @gen_i1() declare <2 x i1> @gen_v2i1() ; Should not be converted to "and", which has different poison semantics. +;. +; CHECK: @g1 = external global i16 +; CHECK: @g2 = external global i16 +;. define i1 @logical_and(i1 %a, i1 %b) { ; CHECK-LABEL: @logical_and( ; CHECK-NEXT: [[RES:%.*]] = select i1 [[A:%.*]], i1 [[B:%.*]], i1 false @@ -225,29 +229,29 @@ define i1 @not_not_true(i1 %x, i1 %y) { ; (!x && !y) --> !(x || y) -define i1 @not_not_false(i1 %x, i1 %y) { +define i1 @not_not_false(i1 %x, i1 %y) !prof !0 { ; CHECK-LABEL: @not_not_false( -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]], !prof [[PROF1:![0-9]+]] ; CHECK-NEXT: [[R:%.*]] = xor i1 [[TMP1]], true ; CHECK-NEXT: ret i1 [[R]] ; %notx = xor i1 %x, true %noty = xor i1 %y, true - %r = select i1 %notx, i1 %noty, i1 false + %r = select i1 %notx, i1 %noty, i1 false, !prof !1 ret i1 %r } ; (!x || !y) --> !(x && y) -define i1 @not_true_not(i1 %x, i1 %y) { +define i1 @not_true_not(i1 %x, i1 %y) !prof !0 { ; CHECK-LABEL: @not_true_not( -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false, !prof [[PROF1]] ; CHECK-NEXT: [[R:%.*]] = xor i1 [[TMP1]], true ; CHECK-NEXT: ret i1 [[R]] ; %notx = xor i1 %x, true %noty = xor i1 %y, true - %r = select i1 %notx, i1 true, i1 %noty + %r = select i1 %notx, i1 true, i1 %noty, !prof !1 ret i1 %r } @@ -1348,3 +1352,12 @@ define i8 @test_logical_commuted_and_ne_a_b(i1 %other_cond, i8 %a, i8 %b) { %select = select i1 %or.cond, i8 %a, i8 %b ret i8 %select } + +!0 = !{!"function_entry_count", i64 1000} +!1 = !{!"branch_weights", i32 2, i32 3} +;. +; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } +;. +; CHECK: [[META0:![0-9]+]] = !{!"function_entry_count", i64 1000} +; CHECK: [[PROF1]] = !{!"branch_weights", i32 3, i32 2} +;. diff --git a/llvm/test/Transforms/InstCombine/select-safe-transforms.ll b/llvm/test/Transforms/InstCombine/select-safe-transforms.ll index d88eaf8aa9ddc..3d97048f43127 100644 --- a/llvm/test/Transforms/InstCombine/select-safe-transforms.ll +++ b/llvm/test/Transforms/InstCombine/select-safe-transforms.ll @@ -58,15 +58,15 @@ define i1 @cond_eq_or_const(i8 %X, i8 %Y) !prof !0 { ret i1 %res } -define i1 @xor_and(i1 %c, i32 %X, i32 %Y) { +define i1 @xor_and(i1 %c, i32 %X, i32 %Y) !prof !0 { ; CHECK-LABEL: @xor_and( ; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 true, i1 [[COMP]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 true, i1 [[COMP]], !prof [[PROF2:![0-9]+]] ; CHECK-NEXT: ret i1 [[SEL]] ; %comp = icmp ult i32 %X, %Y - %sel = select i1 %c, i1 %comp, i1 false + %sel = select i1 %c, i1 %comp, i1 false, !prof !1 %res = xor i1 %sel, true ret i1 %res } @@ -97,15 +97,15 @@ define <2 x i1> @xor_and3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) { ret <2 x i1> %res } -define i1 @xor_or(i1 %c, i32 %X, i32 %Y) { +define i1 @xor_or(i1 %c, i32 %X, i32 %Y) !prof !0 { ; CHECK-LABEL: @xor_or( ; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 [[COMP]], i1 false +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 [[COMP]], i1 false, !prof [[PROF2]] ; CHECK-NEXT: ret i1 [[SEL]] ; %comp = icmp ult i32 %X, %Y - %sel = select i1 %c, i1 true, i1 %comp + %sel = select i1 %c, i1 true, i1 %comp, !prof !1 %res = xor i1 %sel, true ret i1 %res } @@ -802,4 +802,5 @@ define <2 x i1> @not_logical_and2(i1 %b, <2 x i32> %a) { ;. ; CHECK: [[META0:![0-9]+]] = !{!"function_entry_count", i64 1000} ; CHECK: [[PROF1]] = !{!"branch_weights", i32 2, i32 3} +; CHECK: [[PROF2]] = !{!"branch_weights", i32 3, i32 2} ;. From 1640181b3db7a293d1da4524f192f1edd471ddcc Mon Sep 17 00:00:00 2001 From: Alan Zhao Date: Mon, 20 Oct 2025 17:13:43 -0700 Subject: [PATCH 2/2] add the experiment flag --- .../lib/Transforms/InstCombine/InstCombineSelect.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 286b22211725b..5aa8de38e4321 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -3455,16 +3455,18 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { // select a, false, b -> select !a, b, false if (match(TrueVal, m_Specific(Zero))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); + Instruction *MDFrom = ProfcheckDisableMetadataFixes ? nullptr : &SI; SelectInst *NewSI = - SelectInst::Create(NotCond, FalseVal, Zero, "", nullptr, &SI); + SelectInst::Create(NotCond, FalseVal, Zero, "", nullptr, MDFrom); NewSI->swapProfMetadata(); return NewSI; } // select a, b, true -> select !a, true, b if (match(FalseVal, m_Specific(One))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); + Instruction *MDFrom = ProfcheckDisableMetadataFixes ? nullptr : &SI; SelectInst *NewSI = - SelectInst::Create(NotCond, One, TrueVal, "", nullptr, &SI); + SelectInst::Create(NotCond, One, TrueVal, "", nullptr, MDFrom); NewSI->swapProfMetadata(); return NewSI; } @@ -3474,8 +3476,9 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { if (match(&SI, m_LogicalAnd(m_Not(m_Value(A)), m_Not(m_Value(B)))) && (CondVal->hasOneUse() || TrueVal->hasOneUse()) && !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) { + Instruction *MDFrom = ProfcheckDisableMetadataFixes ? nullptr : &SI; SelectInst *NewSI = - cast(Builder.CreateSelect(A, One, B, "", &SI)); + cast(Builder.CreateSelect(A, One, B, "", MDFrom)); NewSI->swapProfMetadata(); return BinaryOperator::CreateNot(NewSI); } @@ -3485,8 +3488,9 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { if (match(&SI, m_LogicalOr(m_Not(m_Value(A)), m_Not(m_Value(B)))) && (CondVal->hasOneUse() || FalseVal->hasOneUse()) && !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr())) { + Instruction *MDFrom = ProfcheckDisableMetadataFixes ? nullptr : &SI; SelectInst *NewSI = - cast(Builder.CreateSelect(A, B, Zero, "", &SI)); + cast(Builder.CreateSelect(A, B, Zero, "", MDFrom)); NewSI->swapProfMetadata(); return BinaryOperator::CreateNot(NewSI); }