[llvm] b89551b - [SimplifyCFG] Check every sinking candidate in canSinkInstructions (#217372)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 11:58:54 PDT 2026


Author: Florian Hahn
Date: 2026-08-19T19:58:50+01:00
New Revision: b89551b079ae57cfb00baae16881e22310ae342a

URL: https://github.com/llvm/llvm-project/commit/b89551b079ae57cfb00baae16881e22310ae342a
DIFF: https://github.com/llvm/llvm-project/commit/b89551b079ae57cfb00baae16881e22310ae342a.diff

LOG: [SimplifyCFG] Check every sinking candidate in canSinkInstructions (#217372)

canReplaceOperandWithVariable() can depend on the operand value (e.g. if
the operand of only some instructions is a swifterror value, or a
constant operand for an operand bundle). Check operands of all
instructions to sink.

Fixes verifier errors after sinking the swifterror examples.

PR: https://github.com/llvm/llvm-project/pull/217372

Added: 
    llvm/test/Transforms/SimplifyCFG/constant-bundle-operand.ll

Modified: 
    llvm/lib/Transforms/Utils/SimplifyCFG.cpp
    llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 089892dc573f3..2f9a4819f9984 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -2303,9 +2303,12 @@ static bool canSinkInstructions(
       return I->getOperand(OI) == I0->getOperand(OI);
     };
     if (!all_of(Insts, SameAsI0)) {
+      auto CanReplaceOperand = [OI](const Instruction *I) {
+        return canReplaceOperandWithVariable(I, OI);
+      };
       if ((isa<Constant>(Op) && !replacingOperandWithVariableIsCheap(I0, OI)) ||
-          !canReplaceOperandWithVariable(I0, OI))
-        // We can't create a PHI from this GEP.
+          !all_of(Insts, CanReplaceOperand))
+        // We can't create a PHI from this operand.
         return false;
       auto &Ops = PHIOperands[&I0->getOperandUse(OI)];
       for (auto *I : Insts)

diff  --git a/llvm/test/Transforms/SimplifyCFG/constant-bundle-operand.ll b/llvm/test/Transforms/SimplifyCFG/constant-bundle-operand.ll
new file mode 100644
index 0000000000000..ec068358099a9
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/constant-bundle-operand.ll
@@ -0,0 +1,110 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='simplifycfg<sink-common-insts>,verify' -S %s | FileCheck %s
+
+declare void @clobber1()
+declare void @clobber2()
+declare void @f()
+
+; Both bundle operands are variable, so the calls can be sunk and the bundle
+; operand merged by a PHI.
+define void @sink_variable_bundle_operands(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: define void @sink_variable_bundle_operands(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[BB:.*:]]
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    call void @clobber1()
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    call void @clobber2()
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[B_SINK:%.*]] = phi i32 [ [[B]], %[[ELSE]] ], [ [[A]], %[[THEN]] ]
+; CHECK-NEXT:    call void @f() [ "deopt"(i32 [[B_SINK]]) ]
+; CHECK-NEXT:    ret void
+;
+bb:
+  br i1 %c, label %then, label %else
+
+then:
+  call void @clobber1()
+  call void @f() [ "deopt"(i32 %a) ]
+  br label %exit
+
+else:
+  call void @clobber2()
+  call void @f() [ "deopt"(i32 %b) ]
+  br label %exit
+
+exit:
+  ret void
+}
+
+; The bundle operand of the call in %then is constant, so the calls must not be
+; sunk.
+define void @no_sink_constant_bundle_operand_in_then(i1 %c, i32 %b) {
+; CHECK-LABEL: define void @no_sink_constant_bundle_operand_in_then(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[BB:.*:]]
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    call void @clobber1()
+; CHECK-NEXT:    call void @f() [ "deopt"(i32 7) ]
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    call void @clobber2()
+; CHECK-NEXT:    call void @f() [ "deopt"(i32 [[B]]) ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+bb:
+  br i1 %c, label %then, label %else
+
+then:
+  call void @clobber1()
+  call void @f() [ "deopt"(i32 7) ]
+  br label %exit
+
+else:
+  call void @clobber2()
+  call void @f() [ "deopt"(i32 %b) ]
+  br label %exit
+
+exit:
+  ret void
+}
+
+; Same as above, with the constant bundle operand in the other predecessor.
+define void @no_sink_constant_bundle_operand_in_else(i1 %c, i32 %b) {
+; CHECK-LABEL: define void @no_sink_constant_bundle_operand_in_else(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[BB:.*:]]
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    call void @clobber1()
+; CHECK-NEXT:    call void @f() [ "deopt"(i32 [[B]]) ]
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    call void @clobber2()
+; CHECK-NEXT:    call void @f() [ "deopt"(i32 7) ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+bb:
+  br i1 %c, label %then, label %else
+
+then:
+  call void @clobber1()
+  call void @f() [ "deopt"(i32 %b) ]
+  br label %exit
+
+else:
+  call void @clobber2()
+  call void @f() [ "deopt"(i32 7) ]
+  br label %exit
+
+exit:
+  ret void
+}

diff  --git a/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll b/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
index 5dff39c1f9a52..382d881caf5c0 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
@@ -41,6 +41,42 @@ exit:
   ret void
 }
 
+; Same as @sink, but with the swifterror store in the other predecessor. Also
+; must not be sunk, as it requires introducing a select for the swifterror
+; pointer operand.
+define swiftcc void @sink_store_in_other_pred(ptr %arg, ptr swifterror %arg1, i1 %c) {
+; CHECK-LABEL: define swiftcc void @sink_store_in_other_pred
+; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK:       then:
+; CHECK-NEXT:    call void @clobber1()
+; CHECK-NEXT:    store ptr null, ptr [[ARG1]], align 8
+; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    call void @clobber2()
+; CHECK-NEXT:    store ptr null, ptr [[ARG]], align 8
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+bb:
+  br i1 %c, label %then, label %else
+
+then:
+  call void @clobber1()
+  store ptr null, ptr %arg1, align 8
+  br label %exit
+
+else:
+  call void @clobber2()
+  store ptr null, ptr %arg, align 8
+  br label %exit
+
+exit:
+  ret void
+}
+
 define swiftcc void @hoist_store(ptr %arg, ptr swifterror %arg1, i1 %c) {
 ; CHECK-LABEL: define swiftcc void @hoist_store
 ; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {
@@ -74,9 +110,8 @@ exit:
   ret void
 }
 
-; FIXME: currently simplifycfg tries to sink the load to the exit block and
-; introduces a select for the pointer operand. This is not allowed for
-; swifterror pointers.
+; Do not try to sink the loads to the exit block, as this requires introducing
+; a select for the pointer operand. This is not allowed for swifterror pointers.
 define swiftcc ptr @sink_load(ptr %arg, ptr swifterror %arg1, i1 %c) {
 ; CHECK-LABEL: define swiftcc ptr @sink_load
 ; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {
@@ -111,6 +146,43 @@ exit:
   %p = phi ptr [ %l1, %then ], [ %l2, %else ]
   ret ptr %p
 }
+
+; Same as @sink_load, but with the swifterror load in the other predecessor.
+define swiftcc ptr @sink_load_in_other_pred(ptr %arg, ptr swifterror %arg1, i1 %c) {
+; CHECK-LABEL: define swiftcc ptr @sink_load_in_other_pred
+; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK:       then:
+; CHECK-NEXT:    call void @clobber1()
+; CHECK-NEXT:    [[L1:%.*]] = load ptr, ptr [[ARG1]], align 8
+; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    call void @clobber2()
+; CHECK-NEXT:    [[L2:%.*]] = load ptr, ptr [[ARG]], align 8
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[P:%.*]] = phi ptr [ [[L1]], [[THEN]] ], [ [[L2]], [[ELSE]] ]
+; CHECK-NEXT:    ret ptr [[P]]
+;
+bb:
+  br i1 %c, label %then, label %else
+
+then:
+  call void @clobber1()
+  %l1 = load ptr, ptr %arg1, align 8
+  br label %exit
+
+else:
+  call void @clobber2()
+  %l2 = load ptr, ptr %arg, align 8
+  br label %exit
+
+exit:
+  %p = phi ptr [ %l1, %then ], [ %l2, %else ]
+  ret ptr %p
+}
+
 define swiftcc ptr @hoist_load(ptr %arg, ptr swifterror %arg1, i1 %c) {
 ; CHECK-LABEL: define swiftcc ptr @hoist_load
 ; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {


        


More information about the llvm-commits mailing list