]> granicus.if.org Git - clang/commitdiff
MS ABI: Implement proper support for setjmp
authorDavid Majnemer <david.majnemer@gmail.com>
Thu, 29 Jan 2015 09:29:21 +0000 (09:29 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Thu, 29 Jan 2015 09:29:21 +0000 (09:29 +0000)
On targets which use the MSVCRT, setjmp is a macro which expands to
_setjmp or _setjmpex.

_setjmp and _setjmpex have a secret, hidden argument which is not listed
in the function prototype on X64 and WoA.  This hidden argument always
seems to be the frame pointer.

_setjmpex isn't used on X86, _setjmp is magically replaced with a call
to _setjmp3.  The second argument is zero for 'normal' setjmp/longjmp
pairs, otherwise it is a count of additional variadic arguments.  This
is used when setjmp appears inside of a try or __try.

It is not safe to use a pointer to setjmp because _setjmp, _setjmpex and
_setmp3 are not compatible with setjmp.

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

include/clang/Basic/Builtins.def
lib/CodeGen/CGBuiltin.cpp
test/CodeGen/ms-setjmp.c [new file with mode: 0644]

index 0526cb4481a0d0b963fdd45d611519ba820e41c3..acfef18d75e25ae2a5f60ed61f3b879b98e21ab1 100644 (file)
@@ -710,6 +710,9 @@ LANGBUILTIN(__noop,           "i.",  "n", ALL_MS_LANGUAGES)
 LANGBUILTIN(__readfsdword,    "ULiULi", "n", ALL_MS_LANGUAGES)
 LANGBUILTIN(__va_start,       "vc**.", "nt", ALL_MS_LANGUAGES)
 
+// Microsoft library builtins.
+LIBBUILTIN(_setjmpex, "iJ", "fj",   "setjmpex.h", ALL_MS_LANGUAGES)
+
 // C99 library functions
 // C99 stdlib.h
 LIBBUILTIN(abort, "v",            "fr",    "stdlib.h", ALL_LANGUAGES)
index ee8aa12b680528b6aee3c26b24907efad27b3818..f192788c787edcec7108188a5404381c77087c5e 100644 (file)
@@ -21,6 +21,7 @@
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/IR/CallSite.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Intrinsics.h"
@@ -1662,6 +1663,57 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
   case Builtin::BI__exception_info:
   case Builtin::BI_exception_info:
     return RValue::get(EmitSEHExceptionInfo());
+  case Builtin::BI_setjmpex: {
+    if (getTarget().getTriple().isOSMSVCRT()) {
+      llvm::Type *ArgTypes[] = {Int8PtrTy, Int8PtrTy};
+      llvm::AttributeSet ReturnsTwiceAttr =
+          AttributeSet::get(getLLVMContext(), llvm::AttributeSet::FunctionIndex,
+                            llvm::Attribute::ReturnsTwice);
+      llvm::Constant *SetJmpEx = CGM.CreateRuntimeFunction(
+          llvm::FunctionType::get(IntTy, ArgTypes, /*isVarArg=*/false),
+          "_setjmpex", ReturnsTwiceAttr);
+      llvm::Value *Buf =
+          Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)), Int8PtrTy);
+      llvm::Value *FrameAddr =
+          Builder.CreateCall(CGM.getIntrinsic(Intrinsic::frameaddress),
+                             ConstantInt::get(Int32Ty, 0));
+      llvm::Value *Args[] = {Buf, FrameAddr};
+      llvm::CallSite CS = EmitRuntimeCallOrInvoke(SetJmpEx, Args);
+      CS.setAttributes(ReturnsTwiceAttr);
+      return RValue::get(CS.getInstruction());
+    }
+  }
+  case Builtin::BI_setjmp: {
+    if (getTarget().getTriple().isOSMSVCRT()) {
+      llvm::AttributeSet ReturnsTwiceAttr =
+          AttributeSet::get(getLLVMContext(), llvm::AttributeSet::FunctionIndex,
+                            llvm::Attribute::ReturnsTwice);
+      llvm::Value *Buf =
+          Builder.CreateBitCast(EmitScalarExpr(E->getArg(0)), Int8PtrTy);
+      llvm::CallSite CS;
+      if (getTarget().getTriple().getArch() == llvm::Triple::x86) {
+        llvm::Type *ArgTypes[] = {Int8PtrTy, IntTy};
+        llvm::Constant *SetJmp3 = CGM.CreateRuntimeFunction(
+            llvm::FunctionType::get(IntTy, ArgTypes, /*isVarArg=*/true),
+            "_setjmp3", ReturnsTwiceAttr);
+        llvm::Value *Count = ConstantInt::get(IntTy, 0);
+        llvm::Value *Args[] = {Buf, Count};
+        CS = EmitRuntimeCallOrInvoke(SetJmp3, Args);
+      } else {
+        llvm::Type *ArgTypes[] = {Int8PtrTy, Int8PtrTy};
+        llvm::Constant *SetJmp = CGM.CreateRuntimeFunction(
+            llvm::FunctionType::get(IntTy, ArgTypes, /*isVarArg=*/false),
+            "_setjmp", ReturnsTwiceAttr);
+        llvm::Value *FrameAddr =
+            Builder.CreateCall(CGM.getIntrinsic(Intrinsic::frameaddress),
+                               ConstantInt::get(Int32Ty, 0));
+        llvm::Value *Args[] = {Buf, FrameAddr};
+        CS = EmitRuntimeCallOrInvoke(SetJmp, Args);
+      }
+      CS.setAttributes(ReturnsTwiceAttr);
+      return RValue::get(CS.getInstruction());
+    }
+  }
   }
 
   // If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/test/CodeGen/ms-setjmp.c b/test/CodeGen/ms-setjmp.c
new file mode 100644 (file)
index 0000000..5073e08
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fms-extensions -triple i686-windows-msvc   -emit-llvm %s -o - | FileCheck --check-prefix=I386 %s
+// RUN: %clang_cc1 -fms-extensions -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck --check-prefix=X64 %s
+
+typedef char jmp_buf[1];
+
+int _setjmp(jmp_buf env);
+int _setjmpex(jmp_buf env);
+
+jmp_buf jb;
+
+int test_setjmp() {
+  return _setjmp(jb);
+  // I386-LABEL: define i32 @test_setjmp
+  // I386:       %[[call:.*]] = call i32 (i8*, i32, ...)* @_setjmp3(i8* getelementptr inbounds ([1 x i8]* @jb, i32 0, i32 0), i32 0)
+  // I386-NEXT:  ret i32 %[[call]]
+
+  // X64-LABEL: define i32 @test_setjmp
+  // X64:       %[[addr:.*]] = call i8* @llvm.frameaddress(i32 0)
+  // X64:       %[[call:.*]] = call i32 @_setjmp(i8* getelementptr inbounds ([1 x i8]* @jb, i32 0, i32 0), i8* %[[addr]])
+  // X64-NEXT:  ret i32 %[[call]]
+}
+
+int test_setjmpex() {
+  return _setjmpex(jb);
+  // X64-LABEL: define i32 @test_setjmpex
+  // X64:       %[[addr:.*]] = call i8* @llvm.frameaddress(i32 0)
+  // X64:       %[[call:.*]] = call i32 @_setjmpex(i8* getelementptr inbounds ([1 x i8]* @jb, i32 0, i32 0), i8* %[[addr]])
+  // X64-NEXT:  ret i32 %[[call]]
+}