Index: llvm/trunk/include/llvm/Analysis/LoopInfoImpl.h =================================================================== --- llvm/trunk/include/llvm/Analysis/LoopInfoImpl.h +++ llvm/trunk/include/llvm/Analysis/LoopInfoImpl.h @@ -91,8 +91,9 @@ /// getLoopPreheader - If there is a preheader for this loop, return it. A /// loop has a preheader if there is only one edge to the header of the loop -/// from outside of the loop. If this is the case, the block branching to the -/// header of the loop is the preheader node. +/// from outside of the loop and it is legal to hoist instructions into the +/// predecessor. If this is the case, the block branching to the header of the +/// loop is the preheader node. /// /// This method returns null if there is no preheader for the loop. /// @@ -102,6 +103,10 @@ BlockT *Out = getLoopPredecessor(); if (!Out) return nullptr; + // Make sure we are allowed to hoist instructions into the predecessor. + if (!Out->isLegalToHoistInto()) + return nullptr; + // Make sure there is only one exit out of the preheader. typedef GraphTraits BlockTraits; typename BlockTraits::ChildIteratorType SI = BlockTraits::child_begin(Out); Index: llvm/trunk/include/llvm/CodeGen/MachineBasicBlock.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/MachineBasicBlock.h +++ llvm/trunk/include/llvm/CodeGen/MachineBasicBlock.h @@ -376,6 +376,9 @@ /// Indicates if this is the entry block of a cleanup funclet. void setIsCleanupFuncletEntry(bool V = true) { IsCleanupFuncletEntry = V; } + /// Returns true if it is legal to hoist instructions into this block. + bool isLegalToHoistInto() const; + // Code Layout methods. /// Move 'this' block before or after the specified block. This only moves Index: llvm/trunk/include/llvm/IR/BasicBlock.h =================================================================== --- llvm/trunk/include/llvm/IR/BasicBlock.h +++ llvm/trunk/include/llvm/IR/BasicBlock.h @@ -395,6 +395,9 @@ static_cast(this)->getLandingPadInst()); } + /// \brief Return true if it is legal to hoist instructions into this block. + bool isLegalToHoistInto() const; + private: /// \brief Increment the internal refcount of the number of BlockAddresses /// referencing this BasicBlock by \p Amt. Index: llvm/trunk/lib/CodeGen/MachineBasicBlock.cpp =================================================================== --- llvm/trunk/lib/CodeGen/MachineBasicBlock.cpp +++ llvm/trunk/lib/CodeGen/MachineBasicBlock.cpp @@ -228,6 +228,12 @@ } #endif +bool MachineBasicBlock::isLegalToHoistInto() const { + if (isReturnBlock() || hasEHPadSuccessor()) + return false; + return true; +} + StringRef MachineBasicBlock::getName() const { if (const BasicBlock *LBB = getBasicBlock()) return LBB->getName(); Index: llvm/trunk/lib/IR/BasicBlock.cpp =================================================================== --- llvm/trunk/lib/IR/BasicBlock.cpp +++ llvm/trunk/lib/IR/BasicBlock.cpp @@ -355,6 +355,19 @@ return true; } +bool BasicBlock::isLegalToHoistInto() const { + auto *Term = getTerminator(); + // No terminator means the block is under construction. + if (!Term) + return true; + + // If the block has no successors, there can be no instructions to hoist. + assert(Term->getNumSuccessors() > 0); + + // Instructions should not be hoisted across exception handling boundaries. + return !Term->isExceptional(); +} + /// This splits a basic block into two at the specified /// instruction. Note that all instructions BEFORE the specified iterator stay /// as part of the original basic block, an unconditional branch is added to Index: llvm/trunk/test/Transforms/LoopRotate/catchret.ll =================================================================== --- llvm/trunk/test/Transforms/LoopRotate/catchret.ll +++ llvm/trunk/test/Transforms/LoopRotate/catchret.ll @@ -0,0 +1,41 @@ +; RUN: opt < %s -loop-rotate -S | FileCheck %s + +target triple = "x86_64-pc-windows-msvc" + +declare void @always_throws() + +define i32 @test() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @always_throws() + to label %continue unwind label %catch.dispatch + +continue: + unreachable + +catch.dispatch: + %t0 = catchswitch within none [label %catch] unwind to caller + +catch: + %t1 = catchpad within %t0 [i8* null, i32 64, i8* null] + catchret from %t1 to label %for.cond + +for.cond: + %sum = phi i32 [ %add, %for.body ], [ 0, %catch ] + %i = phi i32 [ %inc, %for.body ], [ 0, %catch ] + %cmp = icmp slt i32 %i, 1 + br i1 %cmp, label %for.body, label %return + +for.body: + %add = add nsw i32 1, %sum + %inc = add nsw i32 %i, 1 + br label %for.cond + +return: + ret i32 0 +} + +; CHECK: catch: +; CHECK-NEXT: catchpad +; CHECK-NEXT: catchret + +declare i32 @__CxxFrameHandler3(...)