[llvm] c786529 - [SimplifyCFG] Drop UB-implying attributes when hoisting speculatively (#226746)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 28 03:54:30 PDT 2026


Author: Mian Miftah
Date: 2026-09-28T12:54:23+02:00
New Revision: c786529445a1ecb3e8618eba4d68132e40c2e40f

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

LOG: [SimplifyCFG] Drop UB-implying attributes when hoisting speculatively (#226746)

`hoistCommonCodeFromSuccessors()` can hoist identical instructions past
non-identical ones that it skips. If a skipped instruction may throw or
not return, the hoisted instruction is speculated, but it keeps its
UB-implying attributes and metadata, like `noundef`. Drop them in that
case, as SimplifyCFG already does when it speculates.

Added: 
    

Modified: 
    llvm/lib/Transforms/Utils/SimplifyCFG.cpp
    llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 9771b04c418f5..7b31b2ec37a66 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -2053,6 +2053,14 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
         I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
         I2->eraseFromParent();
       }
+      // I1 now executes before the instructions we skipped.
+      unsigned SkippedFlags = 0;
+      for (const SuccIterPair &P : SuccIterPairs)
+        SkippedFlags |= P.second;
+      if (SkippedFlags & SkipImplicitControlFlow) {
+        // One of them may throw or not return, so I1 is speculated.
+        I1->dropUBImplyingAttrsAndMetadata();
+      }
       if (!Changed)
         NumHoistCommonCode += SuccIterPairs.size();
       Changed = true;

diff  --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
index 3e6f31176315b..31a59ab83285e 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
@@ -1150,3 +1150,68 @@ j:
   %p = phi ptr [ %call, %t ], [ %call2, %e ]
   ret ptr %p
 }
+
+declare void @map(i64) nounwind willreturn
+declare i32 @pure(i32) memory(none) nounwind willreturn
+declare i32 @pure_speculatable(i32) memory(none) nounwind willreturn speculatable
+
+; The skipped calls may write memory, but noundef does not depend on it.
+define i32 @hoist_noundef_past_call(i1 %c, i64 %i, i32 %x) {
+; CHECK-LABEL: @hoist_noundef_past_call(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = call noundef i32 @pure(i32 noundef [[X:%.*]])
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK:       if:
+; CHECK-NEXT:    call void @map(i64 [[I:%.*]])
+; CHECK-NEXT:    br label [[END:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    call void @map(i64 0)
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    ret i32 [[A]]
+;
+entry:
+  br i1 %c, label %if, label %else
+if:
+  call void @map(i64 %i)
+  %a = call noundef i32 @pure(i32 noundef %x)
+  br label %end
+else:
+  call void @map(i64 0)
+  %b = call noundef i32 @pure(i32 noundef %x)
+  br label %end
+end:
+  %r = phi i32 [ %a, %if ], [ %b, %else ]
+  ret i32 %r
+}
+
+; The skipped calls may throw, so the hoisted call is speculated and noundef
+; may not hold.
+define i32 @hoist_noundef_past_throwing_call(i1 %c, i32 %x) {
+; CHECK-LABEL: @hoist_noundef_past_throwing_call(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = call i32 @pure_speculatable(i32 [[X:%.*]])
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK:       if:
+; CHECK-NEXT:    call void @side_effects0()
+; CHECK-NEXT:    br label [[END:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    call void @side_effects1()
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    ret i32 [[A]]
+;
+entry:
+  br i1 %c, label %if, label %else
+if:
+  call void @side_effects0()
+  %a = call noundef i32 @pure_speculatable(i32 noundef %x)
+  br label %end
+else:
+  call void @side_effects1()
+  %b = call noundef i32 @pure_speculatable(i32 noundef %x)
+  br label %end
+end:
+  %r = phi i32 [ %a, %if ], [ %b, %else ]
+  ret i32 %r
+}


        


More information about the llvm-commits mailing list