[SEH] Add 32-bit lowering code for __try

This is just the clang-side of 32-bit SEH. LLVM still needs work, and it
will determinstically fail to compile until it's feature complete.

On x86, all outlined handlers have no parameters, but they do implicitly
take the EBP value passed in and use it to address locals of the parent
frame. We model this with llvm.frameaddress(1).

This works (mostly), but __finally block inlining can break it. For now,
we apply the 'noinline' attribute. If we really want to inline __finally
blocks on 32-bit x86, we should teach the inliner how to untangle
frameescape and framerecover.

Promote the error diagnostic from codegen to sema. It now rejects SEH on
non-Windows platforms. LLVM doesn't implement SEH on non-x86 Windows
platforms, but there's nothing preventing it.

llvm-svn: 236052
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 337aaf2..ae47efe 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -20,6 +20,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/TargetBuiltins.h"
 #include "llvm/IR/CallSite.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicInst.h"
@@ -1271,14 +1272,6 @@
 }
 
 void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
-  // FIXME: Implement SEH on other architectures.
-  const llvm::Triple &T = CGM.getTarget().getTriple();
-  if (T.getArch() != llvm::Triple::x86_64 ||
-      !T.isKnownWindowsMSVCEnvironment()) {
-    ErrorUnsupported(&S, "__try statement");
-    return;
-  }
-
   EnterSEHTryStmt(S);
   {
     JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave");
@@ -1303,25 +1296,39 @@
 
   void Emit(CodeGenFunction &CGF, Flags F) override {
     ASTContext &Context = CGF.getContext();
-    QualType ArgTys[2] = {Context.UnsignedCharTy, Context.VoidPtrTy};
-    FunctionProtoType::ExtProtoInfo EPI;
-    const auto *FTP = cast<FunctionType>(
-        Context.getFunctionType(Context.VoidTy, ArgTys, EPI));
-
-    CallArgList Args;
-    llvm::Value *IsForEH =
-        llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup());
-    Args.add(RValue::get(IsForEH), ArgTys[0]);
-
     CodeGenModule &CGM = CGF.CGM;
-    llvm::Value *Zero = llvm::ConstantInt::get(CGM.Int32Ty, 0);
-    llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress);
-    llvm::Value *FP = CGF.Builder.CreateCall(FrameAddr, Zero);
-    Args.add(RValue::get(FP), ArgTys[1]);
 
-    const CGFunctionInfo &FnInfo =
-        CGM.getTypes().arrangeFreeFunctionCall(Args, FTP, /*chainCall=*/false);
-    CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args);
+    // In 64-bit, we call the child function with arguments. In 32-bit, we store
+    // zero in the parent frame and use framerecover to check the value.
+    const CGFunctionInfo *FnInfo;
+    CallArgList Args;
+    if (CGF.getTarget().getTriple().getArch() == llvm::Triple::x86_64) {
+      // Compute the two argument values.
+      QualType ArgTys[2] = {Context.UnsignedCharTy, Context.VoidPtrTy};
+      llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress);
+      llvm::Value *FP =
+          CGF.Builder.CreateCall(FrameAddr, CGF.Builder.getInt32(0));
+      llvm::Value *IsForEH =
+          llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup());
+      Args.add(RValue::get(IsForEH), ArgTys[0]);
+      Args.add(RValue::get(FP), ArgTys[1]);
+
+      // Arrange a two-arg function info and type.
+      FunctionProtoType::ExtProtoInfo EPI;
+      const auto *FPT = cast<FunctionProtoType>(
+          Context.getFunctionType(Context.VoidTy, ArgTys, EPI));
+      FnInfo = &CGM.getTypes().arrangeFreeFunctionCall(Args, FPT,
+                                                       /*chainCall=*/false);
+    } else {
+      // Emit the zero store if this is normal control flow. There are no
+      // explicit arguments.
+      if (F.isForNormalCleanup() && CGF.ChildAbnormalTerminationSlot)
+        CGF.Builder.CreateStore(CGF.Builder.getInt32(0),
+                                CGF.ChildAbnormalTerminationSlot);
+      FnInfo = &CGM.getTypes().arrangeNullaryFunction();
+    }
+
+    CGF.EmitCall(*FnInfo, OutlinedFinally, ReturnValueSlot(), Args);
   }
 };
 }
@@ -1332,6 +1339,7 @@
   CodeGenFunction &ParentCGF;
   const VarDecl *ParentThis;
   SmallVector<const VarDecl *, 4> Captures;
+  llvm::Value *AbnormalTermination = nullptr;
   CaptureFinder(CodeGenFunction &ParentCGF, const VarDecl *ParentThis)
       : ParentCGF(ParentCGF), ParentThis(ParentThis) {}
 
@@ -1358,25 +1366,93 @@
   void VisitCXXThisExpr(const CXXThisExpr *E) {
     Captures.push_back(ParentThis);
   }
+
+  void VisitCallExpr(const CallExpr *E) {
+    // We only need to add parent frame allocations for these builtins in x86.
+    if (ParentCGF.getTarget().getTriple().getArch() != llvm::Triple::x86)
+      return;
+
+    unsigned ID = E->getBuiltinCallee();
+    switch (ID) {
+    case Builtin::BI__abnormal_termination:
+    case Builtin::BI_abnormal_termination:
+      // This is the simple case where we are the outermost finally. All we
+      // have to do here is make sure we escape this and recover it in the
+      // outlined handler.
+      if (!AbnormalTermination)
+        AbnormalTermination = ParentCGF.CreateMemTemp(
+            ParentCGF.getContext().IntTy, "abnormal_termination");
+      break;
+    }
+  }
 };
 }
 
+llvm::Value *CodeGenFunction::recoverAddrOfEscapedLocal(
+    CodeGenFunction &ParentCGF, llvm::Value *ParentVar, llvm::Value *ParentFP) {
+  llvm::CallInst *RecoverCall = nullptr;
+  CGBuilderTy Builder(AllocaInsertPt);
+  if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) {
+    // Mark the variable escaped if nobody else referenced it and compute the
+    // frameescape index.
+    auto InsertPair = ParentCGF.EscapedLocals.insert(
+        std::make_pair(ParentAlloca, ParentCGF.EscapedLocals.size()));
+    int FrameEscapeIdx = InsertPair.first->second;
+    // call i8* @llvm.framerecover(i8* bitcast(@parentFn), i8* %fp, i32 N)
+    llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration(
+        &CGM.getModule(), llvm::Intrinsic::framerecover);
+    llvm::Constant *ParentI8Fn =
+        llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy);
+    RecoverCall =
+        Builder.CreateCall3(FrameRecoverFn, ParentI8Fn, ParentFP,
+                            llvm::ConstantInt::get(Int32Ty, FrameEscapeIdx));
+
+  } else {
+    // If the parent didn't have an alloca, we're doing some nested outlining.
+    // Just clone the existing framerecover call, but tweak the FP argument to
+    // use our FP value. All other arguments are constants.
+    auto *ParentRecover =
+        cast<llvm::IntrinsicInst>(ParentVar->stripPointerCasts());
+    assert(ParentRecover->getIntrinsicID() == llvm::Intrinsic::framerecover &&
+           "expected alloca or framerecover in parent LocalDeclMap");
+    RecoverCall = cast<llvm::CallInst>(ParentRecover->clone());
+    RecoverCall->setArgOperand(1, ParentFP);
+    RecoverCall->insertBefore(AllocaInsertPt);
+  }
+
+  // Bitcast the variable, rename it, and insert it in the local decl map.
+  llvm::Value *ChildVar =
+      Builder.CreateBitCast(RecoverCall, ParentVar->getType());
+  ChildVar->setName(ParentVar->getName());
+  return ChildVar;
+}
+
 void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
-                                         const Stmt *OutlinedStmt,
-                                         llvm::Value *ParentFP) {
+                                         const Stmt *OutlinedStmt) {
   // Find all captures in the Stmt.
   CaptureFinder Finder(ParentCGF, ParentCGF.CXXABIThisDecl);
   Finder.Visit(OutlinedStmt);
 
   // Typically there are no captures and we can exit early.
-  if (Finder.Captures.empty())
+  if (Finder.Captures.empty() && !Finder.AbnormalTermination)
     return;
 
-  // Prepare the first two arguments to llvm.framerecover.
-  llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration(
-      &CGM.getModule(), llvm::Intrinsic::framerecover);
-  llvm::Constant *ParentI8Fn =
-      llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy);
+  // The parent FP is passed in as EBP on x86 and the second argument on x64.
+  llvm::Value *ParentFP;
+  if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) {
+    auto AI = CurFn->arg_begin();
+    ++AI;
+    ParentFP = AI;
+  } else {
+    CGBuilderTy Builder(AllocaInsertPt);
+    ParentFP = Builder.CreateCall(
+        CGM.getIntrinsic(llvm::Intrinsic::frameaddress), Builder.getInt32(1));
+
+    // Inlining will break llvm.frameaddress(1), so disable it.
+    // FIXME: We could teach the inliner about the special meaning of
+    // frameaddress, framerecover, and frameescape to remove this limitation.
+    CurFn->addFnAttr(llvm::Attribute::NoInline);
+  }
 
   // Create llvm.framerecover calls for all captures.
   for (const VarDecl *VD : Finder.Captures) {
@@ -1399,39 +1475,16 @@
       continue;
     llvm::Value *ParentVar = I->second;
 
-    llvm::CallInst *RecoverCall = nullptr;
-    CGBuilderTy Builder(AllocaInsertPt);
-    if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) {
-      // Mark the variable escaped if nobody else referenced it and compute the
-      // frameescape index.
-      auto InsertPair =
-          ParentCGF.EscapedLocals.insert(std::make_pair(ParentAlloca, -1));
-      if (InsertPair.second)
-        InsertPair.first->second = ParentCGF.EscapedLocals.size() - 1;
-      int FrameEscapeIdx = InsertPair.first->second;
-      // call i8* @llvm.framerecover(i8* bitcast(@parentFn), i8* %fp, i32 N)
-      RecoverCall =
-          Builder.CreateCall3(FrameRecoverFn, ParentI8Fn, ParentFP,
-                              llvm::ConstantInt::get(Int32Ty, FrameEscapeIdx));
+    LocalDeclMap[VD] =
+        recoverAddrOfEscapedLocal(ParentCGF, ParentVar, ParentFP);
+  }
 
-    } else {
-      // If the parent didn't have an alloca, we're doing some nested outlining.
-      // Just clone the existing framerecover call, but tweak the FP argument to
-      // use our FP value. All other arguments are constants.
-      auto *ParentRecover =
-          cast<llvm::IntrinsicInst>(ParentVar->stripPointerCasts());
-      assert(ParentRecover->getIntrinsicID() == llvm::Intrinsic::framerecover &&
-             "expected alloca or framerecover in parent LocalDeclMap");
-      RecoverCall = cast<llvm::CallInst>(ParentRecover->clone());
-      RecoverCall->setArgOperand(1, ParentFP);
-      RecoverCall->insertBefore(AllocaInsertPt);
-    }
-
-    // Bitcast the variable, rename it, and insert it in the local decl map.
-    llvm::Value *ChildVar =
-        Builder.CreateBitCast(RecoverCall, ParentVar->getType());
-    ChildVar->setName(ParentVar->getName());
-    LocalDeclMap[VD] = ChildVar;
+  // AbnormalTermination is just another capture, but it has no Decl.
+  if (Finder.AbnormalTermination) {
+    AbnormalTerminationSlot = recoverAddrOfEscapedLocal(
+        ParentCGF, Finder.AbnormalTermination, ParentFP);
+    // Save the slot on the parent so it can store 1 and 0 to it.
+    ParentCGF.ChildAbnormalTerminationSlot = Finder.AbnormalTermination;
   }
 }
 
@@ -1466,10 +1519,7 @@
                 OutlinedStmt->getLocStart(), OutlinedStmt->getLocStart());
 
   CGM.SetLLVMFunctionAttributes(nullptr, FnInfo, CurFn);
-
-  auto AI = Fn->arg_begin();
-  ++AI;
-  EmitCapturedLocals(ParentCGF, OutlinedStmt, &*AI);
+  EmitCapturedLocals(ParentCGF, OutlinedStmt);
 }
 
 /// Create a stub filter function that will ultimately hold the code of the
@@ -1481,14 +1531,16 @@
   const Expr *FilterExpr = Except.getFilterExpr();
   SourceLocation StartLoc = FilterExpr->getLocStart();
 
-  SEHPointersDecl = ImplicitParamDecl::Create(
-      getContext(), nullptr, StartLoc,
-      &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy);
   FunctionArgList Args;
-  Args.push_back(SEHPointersDecl);
-  Args.push_back(ImplicitParamDecl::Create(
-      getContext(), nullptr, StartLoc,
-      &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
+  if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) {
+    SEHPointersDecl = ImplicitParamDecl::Create(
+        getContext(), nullptr, StartLoc,
+        &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy);
+    Args.push_back(SEHPointersDecl);
+    Args.push_back(ImplicitParamDecl::Create(
+        getContext(), nullptr, StartLoc,
+        &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
+  }
 
   // Get the mangled function name.
   SmallString<128> Name;
@@ -1529,13 +1581,15 @@
   SourceLocation StartLoc = FinallyBlock->getLocStart();
 
   FunctionArgList Args;
-  Args.push_back(ImplicitParamDecl::Create(
-      getContext(), nullptr, StartLoc,
-      &getContext().Idents.get("abnormal_termination"),
-      getContext().UnsignedCharTy));
-  Args.push_back(ImplicitParamDecl::Create(
-      getContext(), nullptr, StartLoc,
-      &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
+  if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) {
+    Args.push_back(ImplicitParamDecl::Create(
+        getContext(), nullptr, StartLoc,
+        &getContext().Idents.get("abnormal_termination"),
+        getContext().UnsignedCharTy));
+    Args.push_back(ImplicitParamDecl::Create(
+        getContext(), nullptr, StartLoc,
+        &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
+  }
 
   // Get the mangled function name.
   SmallString<128> Name;
@@ -1567,7 +1621,7 @@
   // };
   // void *exn.slot =
   //     (void *)(uintptr_t)exception_pointers->ExceptionRecord->ExceptionCode;
-  llvm::Value *Ptrs = Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl));
+  llvm::Value *Ptrs = EmitSEHExceptionInfo();
   llvm::Type *RecordTy = CGM.Int32Ty->getPointerTo();
   llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy, nullptr);
   Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy->getPointerTo());
@@ -1582,6 +1636,9 @@
 }
 
 llvm::Value *CodeGenFunction::EmitSEHExceptionInfo() {
+  if (CGM.getTarget().getTriple().getArch() != llvm::Triple::x86_64)
+    return Builder.CreateCall(
+        CGM.getIntrinsic(llvm::Intrinsic::eh_exceptioninfo));
   // Sema should diagnose calling this builtin outside of a filter context, but
   // don't crash if we screw up.
   if (!SEHPointersDecl)
@@ -1599,6 +1656,8 @@
 }
 
 llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() {
+  if (CGM.getTarget().getTriple().getArch() != llvm::Triple::x86_64)
+    return Builder.CreateLoad(AbnormalTerminationSlot);
   // Abnormal termination is just the first parameter to the outlined finally
   // helper.
   auto AI = CurFn->arg_begin();
@@ -1608,9 +1667,15 @@
 void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
   CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
   if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) {
-    // Push a cleanup for __finally blocks.
+    // Outline the finally block.
     llvm::Function *FinallyFunc =
         HelperCGF.GenerateSEHFinallyFunction(*this, *Finally);
+
+    // Store 1 to indicate abnormal termination if an exception is thrown.
+    if (ChildAbnormalTerminationSlot)
+      Builder.CreateStore(Builder.getInt32(1), ChildAbnormalTerminationSlot);
+
+    // Push a cleanup for __finally blocks.
     EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc);
     return;
   }
@@ -1642,6 +1707,7 @@
   // Just pop the cleanup if it's a __finally block.
   if (S.getFinallyHandler()) {
     PopCleanupBlock();
+    ChildAbnormalTerminationSlot = nullptr;
     return;
   }