This patch makes clang add the returns_twice attribute to functions that are known to have it.

The list of functions is taken from llvm's callsFunctionThatReturnsTwice plus the __sigsetjmp as reported Khaled.

The hope is that we can simplify callsFunctionThatReturnsTwice to check only for the attribute.

Is the patch OK?

Would a patch dropping the list from callsFunctionThatReturnsTwice be OK for 3.0 or 3.1?

Thanks,
Rafael
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 73bf981..20dd90e 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -1150,7 +1150,7 @@ protected:
 
     /// Extra information which affects how the function is called, like
     /// regparm and the calling convention.
-    unsigned ExtInfo : 8;
+    unsigned ExtInfo : 9;
 
     /// Whether the function is variadic.  Only used by FunctionProtoType.
     unsigned Variadic : 1;
@@ -2503,33 +2503,32 @@ class FunctionType : public Type {
   /// a FunctionType, although FunctionType does currently use the
   /// same bit-pattern.
   ///
-  // If you add a field (say Foo), other than the obvious places (both,
-  // constructors, compile failures), what you need to update is
-  // * Operator==
+  // If you add a field (say Foo), other than the obvious places (
+  // constructor, compile failures), what you need to update is
   // * getFoo
   // * withFoo
-  // * functionType. Add Foo, getFoo.
-  // * ASTContext::getFooType
+  // * functionType. Add getFoo.
   // * ASTContext::mergeFunctionTypes
-  // * FunctionNoProtoType::Profile
-  // * FunctionProtoType::Profile
-  // * TypePrinter::PrintFunctionProto
+  // * CGFunctionInfo::Profile
+  // * TypePrinter::printFunctionProto
   // * AST read and write
   // * Codegen
   class ExtInfo {
-    // Feel free to rearrange or add bits, but if you go over 8,
+    // Feel free to rearrange or add bits, but if you go over 9,
     // you'll need to adjust both the Bits field below and
     // Type::FunctionTypeBitfields.
 
-    //   |  CC  |noreturn|produces|regparm|
-    //   |0 .. 2|   3    |    4   | 5 .. 7|
+    //   |  CC  |noreturn|produces|returns_twice|regparm|
+    //   |0 .. 2|   3    |    4   |      5      | 6.. 8 |
     //
     // regparm is either 0 (no regparm attribute) or the regparm value+1.
     enum { CallConvMask = 0x7 };
     enum { NoReturnMask = 0x8 };
     enum { ProducesResultMask = 0x10 };
-    enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask),
-           RegParmOffset = 5 }; // Assumed to be the last field
+    enum { ReturnsTwiceMask = 0x20 };
+    enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask |
+                           ReturnsTwiceMask),
+           RegParmOffset = 6 }; // Assumed to be the last field
 
     uint16_t Bits;
 
@@ -2541,10 +2540,11 @@ class FunctionType : public Type {
     // Constructor with no defaults. Use this when you know that you
     // have all the elements (when reading an AST file for example).
     ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
-            bool producesResult) {
+            bool producesResult, bool returnsTwice) {
       assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
       Bits = ((unsigned) cc) |
              (noReturn ? NoReturnMask : 0) |
+             (returnsTwice ? ReturnsTwiceMask : 0) |
              (producesResult ? ProducesResultMask : 0) |
              (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0);
     }
@@ -2553,6 +2553,7 @@ class FunctionType : public Type {
     // function know to use defaults.
     ExtInfo() : Bits(0) {}
 
+    bool getReturnsTwice() const { return Bits & ReturnsTwiceMask; }
     bool getNoReturn() const { return Bits & NoReturnMask; }
     bool getProducesResult() const { return Bits & ProducesResultMask; }
     bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; }
@@ -2574,6 +2575,13 @@ class FunctionType : public Type {
     // Note that we don't have setters. That is by design, use
     // the following with methods instead of mutating these objects.
 
+    ExtInfo withReturnsTwice(bool returnsTwice) const {
+      if (returnsTwice)
+        return ExtInfo(Bits | ReturnsTwiceMask);
+      else
+        return ExtInfo(Bits & ~ReturnsTwiceMask);
+    }
+
     ExtInfo withNoReturn(bool noReturn) const {
       if (noReturn)
         return ExtInfo(Bits | NoReturnMask);
@@ -2632,6 +2640,7 @@ public:
   bool getHasRegParm() const { return getExtInfo().getHasRegParm(); }
   unsigned getRegParmType() const { return getExtInfo().getRegParm(); }
   bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
+  bool getReturnsTwiceAttr() const { return getExtInfo().getReturnsTwice(); }
   CallingConv getCallConv() const { return getExtInfo().getCC(); }
   ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
 
@@ -3209,6 +3218,7 @@ public:
 
     // No operand.
     attr_noreturn,
+    attr_returns_twice,
     attr_cdecl,
     attr_fastcall,
     attr_stdcall,
diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def
index 60bcde3..d3a5eaf 100644
--- a/include/clang/Basic/Builtins.def
+++ b/include/clang/Basic/Builtins.def
@@ -77,6 +77,7 @@
 //          in that it accepts its arguments as a va_list rather than
 //          through an ellipsis
 //  e -> const, but only when -fmath-errno=0
+//  j -> returns_twice (like setjmp)
 //  FIXME: gcc has nonnull
 
 #if defined(BUILTIN) && !defined(LIBBUILTIN)
@@ -427,7 +428,7 @@ BUILTIN(__builtin_return_address, "v*IUi", "n")
 BUILTIN(__builtin_extract_return_addr, "v*v*", "n")
 BUILTIN(__builtin_frame_address, "v*IUi", "n")
 BUILTIN(__builtin_flt_rounds, "i", "nc")
-BUILTIN(__builtin_setjmp, "iv**", "")
+BUILTIN(__builtin_setjmp, "iv**", "j")
 BUILTIN(__builtin_longjmp, "vv**i", "r")
 BUILTIN(__builtin_unwind_init, "v", "")
 BUILTIN(__builtin_eh_return_data_regno, "iIi", "nc")
@@ -672,6 +673,17 @@ LIBBUILTIN(bzero, "vv*z",         "f",     "strings.h", 
ALL_LANGUAGES)
 // POSIX unistd.h
 LIBBUILTIN(_exit, "vi",           "fr",    "unistd.h", ALL_LANGUAGES)
 // POSIX setjmp.h
+
+LIBBUILTIN(_setjmp, "iJ",         "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(__sigsetjmp, "iJ",     "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(setjmp, "iJ",          "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(sigsetjmp, "iJ",       "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(setjmp_syscall, "iJ",  "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(savectx, "iJ",         "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(qsetjmp, "iJ",         "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(vfork, "iJ",           "fj",   "setjmp.h", ALL_LANGUAGES)
+LIBBUILTIN(getcontext, "iJ",      "fj",   "setjmp.h", ALL_LANGUAGES)
+
 LIBBUILTIN(_longjmp, "vJi",       "fr",    "setjmp.h", ALL_LANGUAGES)
 LIBBUILTIN(siglongjmp, "vSJi",    "fr",    "setjmp.h", ALL_LANGUAGES)
 // non-standard but very common
diff --git a/include/clang/Basic/Builtins.h b/include/clang/Basic/Builtins.h
index fbf4ef4..5afa020 100644
--- a/include/clang/Basic/Builtins.h
+++ b/include/clang/Basic/Builtins.h
@@ -103,6 +103,11 @@ public:
     return strchr(GetRecord(ID).Attributes, 'r') != 0;
   }
 
+  /// isReturnsTwice - Return true if we know this builtin can return twice.
+  bool isReturnsTwice(unsigned ID) const {
+    return strchr(GetRecord(ID).Attributes, 'j') != 0;
+  }
+
   /// isLibFunction - Return true if this is a builtin for a libc/libm 
function,
   /// with a "__builtin_" prefix (e.g. __builtin_abs).
   bool isLibFunction(unsigned ID) const {
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 9bb4022..670245f 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -1843,6 +1843,7 @@ public:
   bool CheckRegparmAttr(const AttributeList &attr, unsigned &value);
   bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC);
   bool CheckNoReturnAttr(const AttributeList &attr);
+  bool CheckReturnsTwiceAttr(const AttributeList &attr);
 
   void WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method,
                            bool &IncompleteImpl, unsigned DiagID);
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index b625655..83e945c 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -5586,8 +5586,11 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, 
QualType rhs,
     allRTypes = false;
   // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'.
   bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn();
+  bool ReturnsTwice = lbaseInfo.getReturnsTwice() ||
+    rbaseInfo.getReturnsTwice();
 
-  FunctionType::ExtInfo einfo = lbaseInfo.withNoReturn(NoReturn);
+  FunctionType::ExtInfo einfo = lbaseInfo.withNoReturn(NoReturn)
+    .withReturnsTwice(ReturnsTwice);
 
   if (lproto && rproto) { // two C99 style function prototypes
     assert(!lproto->hasExceptionSpec() && !rproto->hasExceptionSpec() &&
@@ -6349,7 +6352,11 @@ QualType ASTContext::GetBuiltinType(unsigned Id,
          "'.' should only occur at end of builtin type list!");
 
   FunctionType::ExtInfo EI;
-  if (BuiltinInfo.isNoReturn(Id)) EI = EI.withNoReturn(true);
+  if (BuiltinInfo.isNoReturn(Id))
+    EI = EI.withNoReturn(true);
+
+  if (BuiltinInfo.isReturnsTwice(Id))
+    EI = EI.withReturnsTwice(true);
 
   bool Variadic = (TypeStr[0] == '.');
 
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index 73f5cdb..4f3e8ad 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -468,6 +468,10 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
 
     if (D->hasAttr<NoReturnAttr>())
       Proto += " __attribute((noreturn))";
+
+    if (D->hasAttr<ReturnsTwiceAttr>())
+      Proto += " __attribute((returns_twice))";
+
     if (CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D)) {
       bool HasInitializerList = false;
       for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(),
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index fb7b918..c25c7a2 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -453,6 +453,8 @@ void TypePrinter::printFunctionProto(const 
FunctionProtoType *T,
   }
   if (Info.getNoReturn())
     S += " __attribute__((noreturn))";
+  if (Info.getReturnsTwice())
+    S += " __attribute__((returns_twice))";
   if (Info.getRegParm())
     S += " __attribute__((regparm (" +
         llvm::utostr_32(Info.getRegParm()) + ")))";
@@ -510,6 +512,8 @@ void TypePrinter::printFunctionNoProto(const 
FunctionNoProtoType *T,
   S += "()";
   if (T->getNoReturnAttr())
     S += " __attribute__((noreturn))";
+  if (T->getReturnsTwiceAttr())
+    S += " __attribute__((returs_twice))";
   print(T->getResultType(), S);
 }
 
@@ -940,6 +944,7 @@ void TypePrinter::printAttributed(const AttributedType *T,
     break;
 
   case AttributedType::attr_noreturn: S += "noreturn"; break;
+  case AttributedType::attr_returns_twice: S += "returns_twice"; break;
   case AttributedType::attr_cdecl: S += "cdecl"; break;
   case AttributedType::attr_fastcall: S += "fastcall"; break;
   case AttributedType::attr_stdcall: S += "stdcall"; break;
diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp
index f52abef..5f280c7 100644
--- a/lib/CodeGen/CGCall.cpp
+++ b/lib/CodeGen/CGCall.cpp
@@ -260,7 +260,8 @@ const CGFunctionInfo 
&CodeGenTypes::getFunctionInfo(CanQualType ResTy,
     return *FI;
 
   // Construct the function info.
-  FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getProducesResult(),
+  FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getReturnsTwice(),
+                          Info.getProducesResult(),
                           Info.getHasRegParm(), Info.getRegParm(), ResTy,
                           ArgTys.data(), ArgTys.size());
   FunctionInfos.InsertNode(FI, InsertPos);
@@ -290,14 +291,16 @@ const CGFunctionInfo 
&CodeGenTypes::getFunctionInfo(CanQualType ResTy,
 }
 
 CGFunctionInfo::CGFunctionInfo(unsigned _CallingConvention,
-                               bool _NoReturn, bool returnsRetained,
+                               bool _NoReturn, bool _ReturnsTwice,
+                               bool returnsRetained,
                                bool _HasRegParm, unsigned _RegParm,
                                CanQualType ResTy,
                                const CanQualType *ArgTys,
                                unsigned NumArgTys)
   : CallingConvention(_CallingConvention),
     EffectiveCallingConvention(_CallingConvention),
-    NoReturn(_NoReturn), ReturnsRetained(returnsRetained),
+    NoReturn(_NoReturn), ReturnsTwice(_ReturnsTwice),
+    ReturnsRetained(returnsRetained),
     HasRegParm(_HasRegParm), RegParm(_RegParm)
 {
   NumArgs = NumArgTys;
@@ -727,6 +730,9 @@ void CodeGenModule::ConstructAttributeList(const 
CGFunctionInfo &FI,
   if (FI.isNoReturn())
     FuncAttrs |= llvm::Attribute::NoReturn;
 
+  if (FI.isReturnsTwice())
+    FuncAttrs |= llvm::Attribute::ReturnsTwice;
+
   // FIXME: handle sseregparm someday...
   if (TargetDecl) {
     if (TargetDecl->hasAttr<NoThrowAttr>())
diff --git a/lib/CodeGen/CGCall.h b/lib/CodeGen/CGCall.h
index 24ed366..a00dc30 100644
--- a/lib/CodeGen/CGCall.h
+++ b/lib/CodeGen/CGCall.h
@@ -123,6 +123,9 @@ namespace CodeGen {
     /// Whether this function is noreturn.
     bool NoReturn;
 
+    /// Whether this function is returns-twice.
+    bool ReturnsTwice;
+
     /// Whether this function is returns-retained.
     bool ReturnsRetained;
 
@@ -137,7 +140,7 @@ namespace CodeGen {
     typedef const ArgInfo *const_arg_iterator;
     typedef ArgInfo *arg_iterator;
 
-    CGFunctionInfo(unsigned CallingConvention, bool NoReturn,
+    CGFunctionInfo(unsigned CallingConvention, bool NoReturn, bool 
ReturnsTwice,
                    bool ReturnsRetained, bool HasRegParm, unsigned RegParm,
                    CanQualType ResTy,
                    const CanQualType *ArgTys, unsigned NumArgTys);
@@ -151,6 +154,7 @@ namespace CodeGen {
     unsigned  arg_size() const { return NumArgs; }
 
     bool isNoReturn() const { return NoReturn; }
+    bool isReturnsTwice() const { return ReturnsTwice; }
 
     /// In ARR, whether this function retains its return value.  This
     /// is not always reliable for call sites.
@@ -180,6 +184,7 @@ namespace CodeGen {
     void Profile(llvm::FoldingSetNodeID &ID) {
       ID.AddInteger(getCallingConvention());
       ID.AddBoolean(NoReturn);
+      ID.AddBoolean(ReturnsTwice);
       ID.AddBoolean(ReturnsRetained);
       ID.AddBoolean(HasRegParm);
       ID.AddInteger(RegParm);
@@ -195,6 +200,7 @@ namespace CodeGen {
                         Iterator end) {
       ID.AddInteger(Info.getCC());
       ID.AddBoolean(Info.getNoReturn());
+      ID.AddBoolean(Info.getReturnsTwice());
       ID.AddBoolean(Info.getProducesResult());
       ID.AddBoolean(Info.getHasRegParm());
       ID.AddInteger(Info.getRegParm());
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d19023d..31e9917 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1737,6 +1737,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl 
*OldD) {
     RequiresAdjustment = true;
   }
 
+  if (OldTypeInfo.getReturnsTwice() != NewTypeInfo.getReturnsTwice()) {
+    NewTypeInfo = NewTypeInfo.withReturnsTwice(true);
+    RequiresAdjustment = true;
+  }
+
   // Merge regparm attribute.
   if (OldTypeInfo.getHasRegParm() != NewTypeInfo.getHasRegParm() ||
       OldTypeInfo.getRegParm() != NewTypeInfo.getRegParm()) {
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index f76bb58..6474245 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1316,6 +1316,16 @@ bool Sema::CheckNoReturnAttr(const AttributeList &attr) {
   return false;
 }
 
+bool Sema::CheckReturnsTwiceAttr(const AttributeList &attr) {
+  if (attr.hasParameterOrArguments()) {
+    Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
+    attr.setInvalid();
+    return true;
+  }
+
+  return false;
+}
+
 static void handleAnalyzerNoReturnAttr(Sema &S, Decl *D,
                                        const AttributeList &Attr) {
   
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 154b2a8..c7fcc83 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -95,6 +95,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const 
AttributeList &attr,
 
 // Function type attributes.
 #define FUNCTION_TYPE_ATTRS_CASELIST \
+    case AttributeList::AT_returns_twice: \
     case AttributeList::AT_noreturn: \
     case AttributeList::AT_cdecl: \
     case AttributeList::AT_fastcall: \
@@ -2673,6 +2674,8 @@ static AttributeList::Kind 
getAttrListKind(AttributedType::Kind kind) {
     return AttributeList::AT_objc_ownership;
   case AttributedType::attr_noreturn:
     return AttributeList::AT_noreturn;
+  case AttributedType::attr_returns_twice:
+    return AttributeList::AT_returns_twice;
   case AttributedType::attr_cdecl:
     return AttributeList::AT_cdecl;
   case AttributedType::attr_fastcall:
@@ -3479,6 +3482,21 @@ static bool handleFunctionTypeAttr(TypeProcessingState 
&state,
     return true;
   }
 
+  if (attr.getKind() == AttributeList::AT_returns_twice) {
+    if (S.CheckReturnsTwiceAttr(attr))
+      return true;
+
+    // Delay if this is not a function type.
+    if (!unwrapped.isFunctionType())
+      return false;
+
+    // Otherwise we can process right away.
+    FunctionType::ExtInfo EI =
+      unwrapped.get()->getExtInfo().withReturnsTwice(true);
+    type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), 
EI));
+    return true;
+  }
+
   // ns_returns_retained is not always a type attribute, but if we got
   // here, we're treating it as one right now.
   if (attr.getKind() == AttributeList::AT_ns_returns_retained) {
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 361c3b7..9d96d8f 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -3250,13 +3250,13 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
   }
 
   case TYPE_FUNCTION_NO_PROTO: {
-    if (Record.size() != 6) {
+    if (Record.size() != 7) {
       Error("incorrect encoding of no-proto function type");
       return QualType();
     }
     QualType ResultType = readType(*Loc.F, Record, Idx);
     FunctionType::ExtInfo Info(Record[1], Record[2], Record[3],
-                               (CallingConv)Record[4], Record[5]);
+                               (CallingConv)Record[4], Record[5], Record[6]);
     return Context.getFunctionNoProtoType(ResultType, Info);
   }
 
@@ -3268,9 +3268,10 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
                                         /*hasregparm*/ Record[2],
                                         /*regparm*/ Record[3],
                                         static_cast<CallingConv>(Record[4]),
-                                        /*produces*/ Record[5]);
+                                        /*produces*/ Record[5],
+                                        /*returnsTwice*/ Record[6]);
 
-    unsigned Idx = 6;
+    unsigned Idx = 7;
     unsigned NumParams = Record[Idx++];
     SmallVector<QualType, 16> ParamTypes;
     for (unsigned I = 0; I != NumParams; ++I)
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index b31262d..30eb4c4 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -172,6 +172,7 @@ void ASTTypeWriter::VisitFunctionType(const FunctionType 
*T) {
   // FIXME: need to stabilize encoding of calling convention...
   Record.push_back(C.getCC());
   Record.push_back(C.getProducesResult());
+  Record.push_back(C.getReturnsTwice());
 }
 
 void ASTTypeWriter::VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
diff --git a/test/Analysis/security-syntax-checks.m 
b/test/Analysis/security-syntax-checks.m
index a04401b..6fb5b3c 100644
--- a/test/Analysis/security-syntax-checks.m
+++ b/test/Analysis/security-syntax-checks.m
@@ -170,7 +170,7 @@ void test_strcat() {
 //===----------------------------------------------------------------------===
 typedef int __int32_t;
 typedef __int32_t pid_t;
-pid_t vfork(void);
+pid_t vfork(void); //expected-warning{{declaration of built-in function 
'vfork' requires inclusion of the header <setjmp.h>}}
 
 void test_vfork() {
   vfork(); //expected-warning{{Call to function 'vfork' is insecure as it can 
lead to denial of service situations in the parent process.}}
diff --git a/test/CodeGen/function-attributes.c 
b/test/CodeGen/function-attributes.c
index fd98458..6cbf40b 100644
--- a/test/CodeGen/function-attributes.c
+++ b/test/CodeGen/function-attributes.c
@@ -100,3 +100,14 @@ __attribute__ ((returns_twice)) void f17(void);
 __attribute__ ((returns_twice)) void f18(void) {
         f17();
 }
+
+// CHECK: define void @f19()
+// CHECK: {
+// CHECK: call i32 @setjmp(i32* null)
+// CHECK: returns_twice
+// CHECK: ret void
+typedef int jmp_buf[((9 * 2) + 3 + 16)];
+int setjmp(jmp_buf);
+void f19(void) {
+  setjmp(0);
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to