Use WinEHPrepare to outline SEH finally blocks
authorReid Kleckner <reid@kleckner.net>
Wed, 18 Mar 2015 20:26:53 +0000 (20:26 +0000)
committerReid Kleckner <reid@kleckner.net>
Wed, 18 Mar 2015 20:26:53 +0000 (20:26 +0000)
No outlining is necessary for SEH catch blocks. Use the blockaddr of the
handler in place of the usual outlined function.

Reviewers: majnemer, andrew.w.kaylor

Differential Revision: http://reviews.llvm.org/D8370

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@232664 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/WinEHPrepare.cpp
test/CodeGen/WinEH/seh-simple.ll [new file with mode: 0644]

index f68189af399cdcd96cde2f92d46e2873c9ed55d1..383aaf6613f8276cd27bbc65a128bb12cd0703b5 100644 (file)
@@ -27,6 +27,7 @@
 #include "llvm/IR/Module.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/Cloning.h"
@@ -77,8 +78,8 @@ public:
   }
 
 private:
-  bool prepareCPPEHHandlers(Function &F,
-                            SmallVectorImpl<LandingPadInst *> &LPads);
+  bool prepareExceptionHandlers(Function &F,
+                                SmallVectorImpl<LandingPadInst *> &LPads);
   bool outlineHandler(ActionHandler *Action, Function *SrcFn,
                       LandingPadInst *LPad, BasicBlock *StartBB,
                       FrameVarInfoMap &VarInfo);
@@ -88,6 +89,10 @@ private:
                                  VisitedBlockSet &VisitedBlocks);
   CleanupHandler *findCleanupHandler(BasicBlock *StartBB, BasicBlock *EndBB);
 
+  void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB);
+
+  // All fields are reset by runOnFunction.
+  EHPersonality Personality;
   CatchHandlerMapTy CatchHandlerMap;
   CleanupHandlerMapTy CleanupHandlerMap;
   DenseMap<const LandingPadInst *, LandingPadMap>  LPadMaps;
@@ -238,20 +243,22 @@ public:
 class ActionHandler {
 public:
   ActionHandler(BasicBlock *BB, ActionType Type)
-      : StartBB(BB), Type(Type), OutlinedFn(nullptr) {}
+      : StartBB(BB), Type(Type), HandlerBlockOrFunc(nullptr) {}
 
   ActionType getType() const { return Type; }
   BasicBlock *getStartBlock() const { return StartBB; }
 
-  bool hasBeenOutlined() { return OutlinedFn != nullptr; }
+  bool hasBeenProcessed() { return HandlerBlockOrFunc != nullptr; }
 
-  void setOutlinedFunction(Function *F) { OutlinedFn = F; }
-  Function *getOutlinedFunction() { return OutlinedFn; }
+  void setHandlerBlockOrFunc(Constant *F) { HandlerBlockOrFunc = F; }
+  Constant *getHandlerBlockOrFunc() { return HandlerBlockOrFunc; }
 
 private:
   BasicBlock *StartBB;
   ActionType Type;
-  Function *OutlinedFn;
+
+  // Can be either a BlockAddress or a Function depending on the EH personality.
+  Constant *HandlerBlockOrFunc;
 };
 
 class CatchHandler : public ActionHandler {
@@ -326,6 +333,11 @@ FunctionPass *llvm::createWinEHPass(const TargetMachine *TM) {
   return new WinEHPrepare(TM);
 }
 
+// FIXME: Remove this once the backend can handle the prepared IR.
+static cl::opt<bool>
+SEHPrepare("sehprepare", cl::Hidden,
+           cl::desc("Prepare functions with SEH personalities"));
+
 bool WinEHPrepare::runOnFunction(Function &Fn) {
   SmallVector<LandingPadInst *, 4> LPads;
   SmallVector<ResumeInst *, 4> Resumes;
@@ -341,27 +353,24 @@ bool WinEHPrepare::runOnFunction(Function &Fn) {
     return false;
 
   // Classify the personality to see what kind of preparation we need.
-  EHPersonality Pers = classifyEHPersonality(LPads.back()->getPersonalityFn());
+  Personality = classifyEHPersonality(LPads.back()->getPersonalityFn());
 
   // Do nothing if this is not an MSVC personality.
-  if (!isMSVCEHPersonality(Pers))
+  if (!isMSVCEHPersonality(Personality))
     return false;
 
-  // FIXME: This only returns true if the C++ EH handlers were outlined.
-  //        When that code is complete, it should always return whatever
-  //        prepareCPPEHHandlers returns.
-  if (Pers == EHPersonality::MSVC_CXX && prepareCPPEHHandlers(Fn, LPads))
+  if (isAsynchronousEHPersonality(Personality) && !SEHPrepare) {
+    // Replace all resume instructions with unreachable.
+    // FIXME: Remove this once the backend can handle the prepared IR.
+    for (ResumeInst *Resume : Resumes) {
+      IRBuilder<>(Resume).CreateUnreachable();
+      Resume->eraseFromParent();
+    }
     return true;
-
-  // FIXME: SEH Cleanups are unimplemented. Replace them with unreachable.
-  if (Resumes.empty())
-    return false;
-
-  for (ResumeInst *Resume : Resumes) {
-    IRBuilder<>(Resume).CreateUnreachable();
-    Resume->eraseFromParent();
   }
 
+  // If there were any landing pads, prepareExceptionHandlers will make changes.
+  prepareExceptionHandlers(Fn, LPads);
   return true;
 }
 
@@ -371,7 +380,7 @@ bool WinEHPrepare::doFinalization(Module &M) {
 
 void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {}
 
-bool WinEHPrepare::prepareCPPEHHandlers(
+bool WinEHPrepare::prepareExceptionHandlers(
     Function &F, SmallVectorImpl<LandingPadInst *> &LPads) {
   // These containers are used to re-map frame variables that are used in
   // outlined catch and cleanup handlers.  They will be populated as the
@@ -417,9 +426,21 @@ bool WinEHPrepare::prepareCPPEHHandlers(
     mapLandingPadBlocks(LPad, Actions);
 
     for (ActionHandler *Action : Actions) {
-      if (Action->hasBeenOutlined())
+      if (Action->hasBeenProcessed())
         continue;
       BasicBlock *StartBB = Action->getStartBlock();
+
+      // SEH doesn't do any outlining for catches. Instead, pass the handler
+      // basic block addr to llvm.eh.actions and list the block as a return
+      // target.
+      if (isAsynchronousEHPersonality(Personality)) {
+        if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
+          processSEHCatchHandler(CatchAction, StartBB);
+          HandlersOutlined = true;
+          continue;
+        }
+      }
+
       if (outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo)) {
         HandlersOutlined = true;
       }
@@ -440,6 +461,12 @@ bool WinEHPrepare::prepareCPPEHHandlers(
       Invoke->setUnwindDest(NewLPadBB);
     }
 
+    // Replace uses of the old lpad in phis with this block and delete the old
+    // block.
+    LPadBB->replaceSuccessorsPhiUsesWith(NewLPadBB);
+    LPadBB->getTerminator()->eraseFromParent();
+    new UnreachableInst(LPadBB->getContext(), LPadBB);
+
     // Add a call to describe the actions for this landing pad.
     std::vector<Value *> ActionArgs;
     ActionArgs.push_back(NewLPad);
@@ -455,8 +482,8 @@ bool WinEHPrepare::prepareCPPEHHandlers(
       } else {
         ActionArgs.push_back(ConstantInt::get(Int32Type, 1));
       }
-      Constant *HandlerPtr =
-          ConstantExpr::getBitCast(Action->getOutlinedFunction(), Int8PtrType);
+      Constant *HandlerPtr = ConstantExpr::getBitCast(
+          Action->getHandlerBlockOrFunc(), Int8PtrType);
       ActionArgs.push_back(HandlerPtr);
     }
     CallInst *Recover =
@@ -656,11 +683,10 @@ bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn,
   LandingPadMap &LPadMap = LPadMaps[LPad];
   if (!LPadMap.isInitialized())
     LPadMap.mapLandingPad(LPad);
-  if (Action->getType() == Catch) {
-    Constant *SelectorType = cast<CatchHandler>(Action)->getSelector();
-    Director.reset(
-        new WinEHCatchDirector(Handler, SelectorType, VarInfo, LPadMap));
-    LPadMap.remapSelector(VMap, ConstantInt::get( Type::getInt32Ty(Context), 1));
+  if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
+    Constant *Sel = CatchAction->getSelector();
+    Director.reset(new WinEHCatchDirector(Handler, Sel, VarInfo, LPadMap));
+    LPadMap.remapSelector(VMap, ConstantInt::get(Type::getInt32Ty(Context), 1));
   } else {
     Director.reset(new WinEHCleanupDirector(Handler, VarInfo, LPadMap));
   }
@@ -687,11 +713,38 @@ bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn,
     CatchAction->setReturnTargets(CatchDirector->getReturnTargets());
   }
 
-  Action->setOutlinedFunction(Handler);
+  Action->setHandlerBlockOrFunc(Handler);
 
   return true;
 }
 
+/// This BB must end in a selector dispatch. All we need to do is pass the
+/// handler block to llvm.eh.actions and list it as a possible indirectbr
+/// target.
+void WinEHPrepare::processSEHCatchHandler(CatchHandler *CatchAction,
+                                          BasicBlock *StartBB) {
+  BasicBlock *HandlerBB;
+  BasicBlock *NextBB;
+  Constant *Selector;
+  bool Res = isSelectorDispatch(StartBB, HandlerBB, Selector, NextBB);
+  if (Res) {
+    // If this was EH dispatch, this must be a conditional branch to the handler
+    // block.
+    // FIXME: Handle instructions in the dispatch block. Currently we drop them,
+    // leading to crashes if some optimization hoists stuff here.
+    assert(CatchAction->getSelector() && HandlerBB &&
+           "expected catch EH dispatch");
+  } else {
+    // This must be a catch-all. Split the block after the landingpad.
+    assert(CatchAction->getSelector()->isNullValue() && "expected catch-all");
+    HandlerBB =
+        StartBB->splitBasicBlock(StartBB->getFirstInsertionPt(), "catch.all");
+  }
+  CatchAction->setHandlerBlockOrFunc(BlockAddress::get(HandlerBB));
+  TinyPtrVector<BasicBlock *> Targets(HandlerBB);
+  CatchAction->setReturnTargets(Targets);
+}
+
 void LandingPadMap::mapLandingPad(const LandingPadInst *LPad) {
   // Each instance of this class should only ever be used to map a single
   // landing pad.
@@ -1115,12 +1168,18 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad,
       // The catch all must occur last.
       assert(HandlersFound == NumClauses - 1);
 
-      // See if there is any interesting code executed before the catch.
-      if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
-        //   Add a cleanup entry to the list
-        Actions.insertCleanupHandler(CleanupAction);
-        DEBUG(dbgs() << "  Found cleanup code in block "
-                     << CleanupAction->getStartBlock()->getName() << "\n");
+      // For C++ EH, check if there is any interesting cleanup code before we
+      // begin the catch. This is important because cleanups cannot rethrow
+      // exceptions but code called from catches can. For SEH, it isn't
+      // important if some finally code before a catch-all is executed out of
+      // line or after recovering from the exception.
+      if (Personality == EHPersonality::MSVC_CXX) {
+        if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
+          //   Add a cleanup entry to the list
+          Actions.insertCleanupHandler(CleanupAction);
+          DEBUG(dbgs() << "  Found cleanup code in block "
+                       << CleanupAction->getStartBlock()->getName() << "\n");
+        }
       }
 
       // Add the catch handler to the action list.
@@ -1130,7 +1189,10 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad,
       Actions.insertCatchHandler(Action);
       DEBUG(dbgs() << "  Catch all handler at block " << BB->getName() << "\n");
       ++HandlersFound;
-      continue;
+
+      // Once we reach a catch-all, don't expect to hit a resume instruction.
+      BB = nullptr;
+      break;
     }
 
     CatchHandler *CatchAction = findCatchHandler(BB, NextBB, VisitedBlocks);
@@ -1155,7 +1217,8 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad,
     BB = NextBB;
   }
 
-  // See if there is any interesting code executed before the resume.
+  // If we didn't wind up in a catch-all, see if there is any interesting code
+  // executed before the resume.
   if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
     //   Add a cleanup entry to the list
     Actions.insertCleanupHandler(CleanupAction);
@@ -1306,8 +1369,13 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB,
     if (auto *Resume = dyn_cast<ResumeInst>(Terminator)) {
       InsertValueInst *Insert1 = nullptr;
       InsertValueInst *Insert2 = nullptr;
-      if (!isa<PHINode>(Resume->getOperand(0))) {
-        Insert2 = dyn_cast<InsertValueInst>(Resume->getOperand(0));
+      Value *ResumeVal = Resume->getOperand(0);
+      // If there is only one landingpad, we may use the lpad directly with no
+      // insertions.
+      if (isa<LandingPadInst>(ResumeVal))
+        return nullptr;
+      if (!isa<PHINode>(ResumeVal)) {
+        Insert2 = dyn_cast<InsertValueInst>(ResumeVal);
         if (!Insert2)
           return createCleanupHandler(CleanupHandlerMap, BB);
         Insert1 = dyn_cast<InsertValueInst>(Insert2->getAggregateOperand());
diff --git a/test/CodeGen/WinEH/seh-simple.ll b/test/CodeGen/WinEH/seh-simple.ll
new file mode 100644 (file)
index 0000000..152dfa3
--- /dev/null
@@ -0,0 +1,138 @@
+; RUN: opt -S -winehprepare -sehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+declare void @cleanup()
+declare i32 @filt()
+declare void @might_crash()
+declare i32 @__C_specific_handler(...)
+declare i32 @llvm.eh.typeid.for(i8*)
+
+define i32 @simple_except_store() {
+entry:
+  %retval = alloca i32
+  store i32 0, i32* %retval
+  invoke void @might_crash()
+          to label %return unwind label %lpad
+
+lpad:
+  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+          catch i32 ()* @filt
+  %sel = extractvalue { i8*, i32 } %ehvals, 1
+  %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+  %matches = icmp eq i32 %sel, %filt_sel
+  br i1 %matches, label %__except, label %eh.resume
+
+__except:
+  store i32 1, i32* %retval
+  br label %return
+
+return:
+  %r = load i32, i32* %retval
+  ret i32 %r
+
+eh.resume:
+  resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @simple_except_store()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@simple_except_store, %__except))
+; CHECK-NEXT: indirectbr {{.*}} [label %__except]
+
+define i32 @catch_all() {
+entry:
+  %retval = alloca i32
+  store i32 0, i32* %retval
+  invoke void @might_crash()
+          to label %return unwind label %lpad
+
+lpad:
+  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+          catch i8* null
+  store i32 1, i32* %retval
+  br label %return
+
+return:
+  %r = load i32, i32* %retval
+  ret i32 %r
+}
+
+; CHECK-LABEL: define i32 @catch_all()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i8* null
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* null, i8* null, i8* blockaddress(@catch_all, %catch.all))
+; CHECK-NEXT: indirectbr {{.*}} [label %catch.all]
+;
+; CHECK: catch.all:
+; CHECK: store i32 1, i32* %retval
+
+
+define i32 @except_phi() {
+entry:
+  invoke void @might_crash()
+          to label %return unwind label %lpad
+
+lpad:
+  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+          catch i32 ()* @filt
+  %sel = extractvalue { i8*, i32 } %ehvals, 1
+  %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+  %matches = icmp eq i32 %sel, %filt_sel
+  br i1 %matches, label %return, label %eh.resume
+
+return:
+  %r = phi i32 [0, %entry], [1, %lpad]
+  ret i32 %r
+
+eh.resume:
+  resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @except_phi()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@except_phi, %return))
+; CHECK-NEXT: indirectbr {{.*}} [label %return]
+;
+; CHECK: return:
+; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad1 ]
+; CHECK-NEXT: ret i32 %r
+
+define i32 @cleanup_and_except() {
+entry:
+  invoke void @might_crash()
+          to label %return unwind label %lpad
+
+lpad:
+  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+          cleanup
+          catch i32 ()* @filt
+  call void @cleanup()
+  %sel = extractvalue { i8*, i32 } %ehvals, 1
+  %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+  %matches = icmp eq i32 %sel, %filt_sel
+  br i1 %matches, label %return, label %eh.resume
+
+return:
+  %r = phi i32 [0, %entry], [1, %lpad]
+  ret i32 %r
+
+eh.resume:
+  resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @cleanup_and_except()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(
+; CHECK: i32 1, i8* bitcast (void (i8*, i8*)* @cleanup_and_except.cleanup to i8*),
+; CHECK: i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@cleanup_and_except, %return))
+; CHECK-NEXT: indirectbr {{.*}} [label %return]
+;
+; CHECK: return:
+; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad1 ]
+; CHECK-NEXT: ret i32 %r