From 0aaadda8e18ef6cf8fd65fad649f5a21b4070c3c Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Thu, 29 Jan 2015 09:29:21 +0000 Subject: [PATCH] MS ABI: Implement proper support for setjmp 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 | 3 ++ lib/CodeGen/CGBuiltin.cpp | 52 ++++++++++++++++++++++++++++++++ test/CodeGen/ms-setjmp.c | 29 ++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 test/CodeGen/ms-setjmp.c diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def index 0526cb4481..acfef18d75 100644 --- a/include/clang/Basic/Builtins.def +++ b/include/clang/Basic/Builtins.def @@ -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) diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index ee8aa12b68..f192788c78 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -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 index 0000000000..5073e08773 --- /dev/null +++ b/test/CodeGen/ms-setjmp.c @@ -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]] +} -- 2.50.1