From c539eea7c6d10abf894c2e4026a5e734882d5d59 Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Thu, 22 Jun 2017 23:27:16 +0000 Subject: [PATCH] Restrict the definition of loop preheader to avoid EH blocks Differential Revision: https://reviews.llvm.org/D34487 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@306070 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/LoopInfoImpl.h | 9 ++++-- include/llvm/CodeGen/MachineBasicBlock.h | 3 ++ include/llvm/IR/BasicBlock.h | 3 ++ lib/CodeGen/MachineBasicBlock.cpp | 6 ++++ lib/IR/BasicBlock.cpp | 13 ++++++++ test/Transforms/LoopRotate/catchret.ll | 41 ++++++++++++++++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) create mode 100755 test/Transforms/LoopRotate/catchret.ll diff --git a/include/llvm/Analysis/LoopInfoImpl.h b/include/llvm/Analysis/LoopInfoImpl.h index 6ff4335f1ad..372fc8b2174 100644 --- a/include/llvm/Analysis/LoopInfoImpl.h +++ b/include/llvm/Analysis/LoopInfoImpl.h @@ -91,8 +91,9 @@ getExitEdges(SmallVectorImpl &ExitEdges) const { /// 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 *LoopBase::getLoopPreheader() const { 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); diff --git a/include/llvm/CodeGen/MachineBasicBlock.h b/include/llvm/CodeGen/MachineBasicBlock.h index 051908c40df..97a49ce4dc4 100644 --- a/include/llvm/CodeGen/MachineBasicBlock.h +++ b/include/llvm/CodeGen/MachineBasicBlock.h @@ -376,6 +376,9 @@ public: /// 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 diff --git a/include/llvm/IR/BasicBlock.h b/include/llvm/IR/BasicBlock.h index 74ad1930909..7a35afcbafc 100644 --- a/include/llvm/IR/BasicBlock.h +++ b/include/llvm/IR/BasicBlock.h @@ -395,6 +395,9 @@ public: 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. diff --git a/lib/CodeGen/MachineBasicBlock.cpp b/lib/CodeGen/MachineBasicBlock.cpp index 590acc01008..81597afe6b0 100644 --- a/lib/CodeGen/MachineBasicBlock.cpp +++ b/lib/CodeGen/MachineBasicBlock.cpp @@ -228,6 +228,12 @@ LLVM_DUMP_METHOD void MachineBasicBlock::dump() const { } #endif +bool MachineBasicBlock::isLegalToHoistInto() const { + if (isReturnBlock() || hasEHPadSuccessor()) + return false; + return true; +} + StringRef MachineBasicBlock::getName() const { if (const BasicBlock *LBB = getBasicBlock()) return LBB->getName(); diff --git a/lib/IR/BasicBlock.cpp b/lib/IR/BasicBlock.cpp index 1f8659d4e2c..2b780adf6c6 100644 --- a/lib/IR/BasicBlock.cpp +++ b/lib/IR/BasicBlock.cpp @@ -355,6 +355,19 @@ bool BasicBlock::canSplitPredecessors() const { 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 diff --git a/test/Transforms/LoopRotate/catchret.ll b/test/Transforms/LoopRotate/catchret.ll new file mode 100755 index 00000000000..c035e49d79c --- /dev/null +++ b/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(...)