Index: llvm/lib/Transforms/Scalar/LoopPredication.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LoopPredication.cpp +++ llvm/lib/Transforms/Scalar/LoopPredication.cpp @@ -1185,20 +1185,8 @@ !Rewriter.isSafeToExpandAt(MinEC, WidenableBR)) return ChangedLoop; - // Subtlety: We need to avoid inserting additional uses of the WC. We know - // that it can only have one transitive use at the moment, and thus moving - // that use to just before the branch and inserting code before it and then - // modifying the operand is legal. - auto *IP = cast(WidenableBR->getCondition()); - // Here we unconditionally modify the IR, so after this point we should return - // only `true`! - IP->moveBefore(WidenableBR); - if (MSSAU) - if (auto *MUD = MSSAU->getMemorySSA()->getMemoryAccess(IP)) - MSSAU->moveToPlace(MUD, WidenableBR->getParent(), - MemorySSA::BeforeTerminator); - Rewriter.setInsertPoint(IP); - IRBuilder<> B(IP); + Rewriter.setInsertPoint(WidenableBR); + IRBuilder<> B(WidenableBR); bool InvalidateLoop = false; Value *MinECV = nullptr; // lazily generated if needed Index: llvm/test/Transforms/LoopPredication/pr61673.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/LoopPredication/pr61673.ll @@ -0,0 +1,73 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -S -passes=loop-predication < %s 2>&1 | FileCheck %s + +; DO NOT sink widenable condition into the loop. It will create extra uses, which leads to miscompile. +; See detailed explanation at https://github.com/llvm/llvm-project/issues/61673. +define i32 @test() { +; CHECK-LABEL: define i32 @test() { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: br label [[OUTER_LOOP:%.*]] +; CHECK: outer_backedge.loopexit: +; CHECK-NEXT: br label [[OUTER_BACKEDGE:%.*]] +; CHECK: outer_backedge: +; CHECK-NEXT: call void @print() +; CHECK-NEXT: [[CNT_NEXT:%.*]] = add i32 [[CNT:%.*]], 1 +; CHECK-NEXT: br label [[OUTER_LOOP]] +; CHECK: outer_loop: +; CHECK-NEXT: [[CNT]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[CNT_NEXT]], [[OUTER_BACKEDGE]] ] +; CHECK-NEXT: br i1 [[WC]], label [[INNER_LOOP_PREHEADER:%.*]], label [[EXIT:%.*]] +; CHECK: inner_loop.preheader: +; CHECK-NEXT: br label [[INNER_LOOP:%.*]] +; CHECK: inner_loop: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[NEVER:%.*]] ], [ 2, [[INNER_LOOP_PREHEADER]] ] +; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; CHECK-NEXT: br i1 false, label [[NEVER]], label [[ALWAYS:%.*]] +; CHECK: never: +; CHECK-NEXT: [[ICMP:%.*]] = icmp ugt i32 [[IV_NEXT]], 37 +; CHECK-NEXT: br i1 [[ICMP]], label [[OUTER_BACKEDGE_LOOPEXIT:%.*]], label [[INNER_LOOP]] +; CHECK: exit: +; CHECK-NEXT: [[CNT_LCSSA:%.*]] = phi i32 [ [[CNT]], [[OUTER_LOOP]] ] +; CHECK-NEXT: ret i32 [[CNT_LCSSA]] +; CHECK: always: +; CHECK-NEXT: br label [[OUTER_BACKEDGE]] +; +entry: + %wc = call i1 @llvm.experimental.widenable.condition() + br label %outer_loop + +outer_backedge: ; preds = %always, %never + call void @print() + %cnt.next = add i32 %cnt, 1 + br label %outer_loop + +outer_loop: ; preds = %outer_backedge, %entry + %cnt = phi i32 [0, %entry], [%cnt.next, %outer_backedge] + br i1 %wc, label %inner_loop.preheader, label %exit + +inner_loop.preheader: ; preds = %outer_loop + br label %inner_loop + +inner_loop: ; preds = %never, %inner_loop.preheader + %iv = phi i32 [ %iv.next, %never ], [ 2, %inner_loop.preheader ] + %iv.next = add nuw nsw i32 %iv, 1 + br i1 false, label %never, label %always + +never: ; preds = %inner_loop + %icmp = icmp ugt i32 %iv.next, 37 + br i1 %icmp, label %outer_backedge, label %inner_loop + +exit: ; preds = %outer_loop + %cnt.lcssa = phi i32 [%cnt, %outer_loop] + ret i32 %cnt.lcssa + +always: ; preds = %inner_loop + br label %outer_backedge +} + +declare void @print() + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) +declare noundef i1 @llvm.experimental.widenable.condition() #0 + +attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } Index: llvm/test/Transforms/LoopPredication/predicate-exits.ll =================================================================== --- llvm/test/Transforms/LoopPredication/predicate-exits.ll +++ llvm/test/Transforms/LoopPredication/predicate-exits.ll @@ -834,12 +834,12 @@ define i32 @trivial_wb(ptr %array, i32 %length, i32 %n) { ; CHECK-LABEL: @trivial_wb( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[N:%.*]], i32 1) ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[UMAX]], -1 ; CHECK-NEXT: [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[LENGTH:%.*]], i32 [[TMP0]]) ; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[LENGTH]], [[UMIN]] ; CHECK-NEXT: [[TMP2:%.*]] = freeze i1 [[TMP1]] -; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[TMP3]], label [[LOOP_PREHEADER:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]] ; CHECK: deopt: @@ -1085,8 +1085,8 @@ define void @test_memssa() { ; CHECK-LABEL: @test_memssa( ; CHECK-NEXT: bb: -; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[TMP:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: br i1 [[TMP]], label [[BB3:%.*]], label [[BB2:%.*]] ; CHECK: bb2: ; CHECK-NEXT: unreachable