* Nuke `__builtin_ms_va_arg`. GCC doesn't have it; they use the normal 
`__builtin_va_arg` instead. (Yes, this is horrifying.)
  * Add tests for serialization and templates.

Hi rsmith, rnk, rjmccall,

http://llvm-reviews.chandlerc.com/D1623

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1623?vs=4119&id=4153#toc

Files:
  include/clang/AST/ASTContext.h
  include/clang/AST/Expr.h
  include/clang/Basic/BuiltinsX86.def
  include/clang/Basic/TargetInfo.h
  include/clang/Sema/Sema.h
  include/clang/Serialization/ASTBitCodes.h
  lib/AST/ASTContext.cpp
  lib/AST/ASTDiagnostic.cpp
  lib/Basic/TargetInfo.cpp
  lib/Basic/Targets.cpp
  lib/CodeGen/CGBuiltin.cpp
  lib/CodeGen/CGCall.cpp
  lib/CodeGen/CGExprAgg.cpp
  lib/CodeGen/CGExprComplex.cpp
  lib/CodeGen/CGExprScalar.cpp
  lib/CodeGen/CodeGenFunction.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/TargetInfo.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaChecking.cpp
  lib/Sema/SemaExpr.cpp
  lib/Serialization/ASTReader.cpp
  lib/Serialization/ASTReaderStmt.cpp
  lib/Serialization/ASTWriter.cpp
  lib/Serialization/ASTWriterStmt.cpp
  test/CodeGen/ms_abi.c
  test/PCH/Inputs/va_arg.h
  test/PCH/va_arg.c
  test/PCH/va_arg.cpp
  test/PCH/va_arg.h
  test/Sema/varargs-x86-64.c
  test/SemaTemplate/instantiate-expr-3.cpp
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -205,6 +205,10 @@
   /// __builtin_va_list type.
   mutable TypedefDecl *BuiltinVaListDecl;
 
+  /// \brief The typedef for the predefined \c __builtin_ms_va_list
+  /// type.
+  mutable TypedefDecl *BuiltinMSVaListDecl;
+
   /// \brief The typedef for the predefined \c id type.
   mutable TypedefDecl *ObjCIdDecl;
   
@@ -1442,6 +1446,15 @@
   /// for some targets.
   QualType getVaListTagType() const;
 
+  /// \brief Retrieve the C type declaration corresponding to the predefined
+  /// \c __builtin_ms_va_list type.
+  TypedefDecl *getBuiltinMSVaListDecl() const;
+
+  /// \brief Retrieve the type of the \c __builtin_ms_va_list type.
+  QualType getBuiltinMSVaListType() const {
+    return getTypeDeclType(getBuiltinMSVaListDecl());
+  }
+
   /// \brief Return a type with additional \c const, \c volatile, or
   /// \c restrict qualifiers.
   QualType getCVRQualifiedType(QualType T, unsigned CVR) const {
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -3582,33 +3582,38 @@
   child_range children() { return child_range(); }
 };
 
-/// VAArgExpr, used for the builtin function __builtin_va_arg.
+/// \brief Represents a call to the builtin function \c __builtin_va_arg.
 class VAArgExpr : public Expr {
-  Stmt *Val;
-  TypeSourceInfo *TInfo;
+  Stmt * Val;
+  llvm::PointerIntPair<TypeSourceInfo *, 1, bool> TInfo;
   SourceLocation BuiltinLoc, RParenLoc;
 public:
   VAArgExpr(SourceLocation BLoc, Expr* e, TypeSourceInfo *TInfo,
-            SourceLocation RPLoc, QualType t)
+            SourceLocation RPLoc, QualType t, bool IsMS = false)
     : Expr(VAArgExprClass, t, VK_RValue, OK_Ordinary,
            t->isDependentType(), false,
            (TInfo->getType()->isInstantiationDependentType() ||
             e->isInstantiationDependent()),
            (TInfo->getType()->containsUnexpandedParameterPack() ||
             e->containsUnexpandedParameterPack())),
-      Val(e), TInfo(TInfo),
+      Val(e), TInfo(TInfo, IsMS),
       BuiltinLoc(BLoc),
       RParenLoc(RPLoc) { }
 
   /// \brief Create an empty __builtin_va_arg expression.
-  explicit VAArgExpr(EmptyShell Empty) : Expr(VAArgExprClass, Empty) { }
+  explicit VAArgExpr(EmptyShell Empty, bool IsMS = false)
+    : Expr(VAArgExprClass, Empty), Val(0), TInfo(0, IsMS) { }
 
   const Expr *getSubExpr() const { return cast<Expr>(Val); }
   Expr *getSubExpr() { return cast<Expr>(Val); }
   void setSubExpr(Expr *E) { Val = E; }
 
-  TypeSourceInfo *getWrittenTypeInfo() const { return TInfo; }
-  void setWrittenTypeInfo(TypeSourceInfo *TI) { TInfo = TI; }
+  /// \brief Returns whether this is really a Win64 ABI va_arg expression.
+  bool isMicrosoftABI() const { return TInfo.getInt(); }
+  void setIsMicrosoftABI(bool IsMS) { TInfo.setInt(IsMS); }
+
+  TypeSourceInfo *getWrittenTypeInfo() const { return TInfo.getPointer(); }
+  void setWrittenTypeInfo(TypeSourceInfo *TI) { TInfo.setPointer(TI); }
 
   SourceLocation getBuiltinLoc() const { return BuiltinLoc; }
   void setBuiltinLoc(SourceLocation L) { BuiltinLoc = L; }
Index: include/clang/Basic/BuiltinsX86.def
===================================================================
--- include/clang/Basic/BuiltinsX86.def
+++ include/clang/Basic/BuiltinsX86.def
@@ -746,4 +746,9 @@
 BUILTIN(__builtin_ia32_xabort, "vIc", "")
 BUILTIN(__builtin_ia32_xtest, "i", "")
 
+// Win64-compatible va_list functions
+BUILTIN(__builtin_ms_va_start, "vc*&.", "n")
+BUILTIN(__builtin_ms_va_end, "vc*&", "n")
+BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n")
+
 #undef BUILTIN
Index: include/clang/Basic/TargetInfo.h
===================================================================
--- include/clang/Basic/TargetInfo.h
+++ include/clang/Basic/TargetInfo.h
@@ -85,6 +85,8 @@
   unsigned RealTypeUsesObjCFPRet : 3;
   unsigned ComplexLongDoubleUsesFP2Ret : 1;
 
+  unsigned HasBuiltinMSVaList : 1;
+
   // TargetInfo Constructor.  Default initializes all fields.
   TargetInfo(const llvm::Triple &T);
 
@@ -456,6 +458,10 @@
   /// with this target.
   virtual BuiltinVaListKind getBuiltinVaListKind() const = 0;
 
+  /// \brief Returns whether or not type \c __builtin_ms_va_list type is
+  /// available on this target.
+  bool hasBuiltinMSVaList() const { return HasBuiltinMSVaList; }
+
   /// \brief Returns whether the passed in string is a valid clobber in an
   /// inline asm statement.
   ///
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -7681,6 +7681,7 @@
   bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
   bool CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
   bool CheckMipsBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
+  bool CheckX86_64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
 
   bool SemaBuiltinVAStart(CallExpr *TheCall);
   bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -906,14 +906,17 @@
       PREDEF_DECL_OBJC_INSTANCETYPE_ID = 8,
 
       /// \brief The internal '__builtin_va_list' typedef.
-      PREDEF_DECL_BUILTIN_VA_LIST_ID = 9
+      PREDEF_DECL_BUILTIN_VA_LIST_ID = 9,
+
+      /// \brief The internal \c __builtin_ms_va_list typedef.
+      PREDEF_DECL_BUILTIN_MS_VA_LIST_ID = 10
     };
 
     /// \brief The number of declaration IDs that are predefined.
     ///
     /// For more information about predefined declarations, see the
     /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants.
-    const unsigned int NUM_PREDEF_DECL_IDS = 10;
+    const unsigned int NUM_PREDEF_DECL_IDS = 11;
     
     /// \brief Record codes for each kind of declaration.
     ///
@@ -1336,7 +1339,8 @@
       EXPR_OBJC_BRIDGED_CAST,     // ObjCBridgedCastExpr
       
       STMT_MS_DEPENDENT_EXISTS,   // MSDependentExistsStmt
-      EXPR_LAMBDA                 // LambdaExpr
+      EXPR_LAMBDA,                // LambdaExpr
+      EXPR_MS_VA_ARG              // VAArgExpr (with isMicrosoftABI() true).
     };
 
     /// \brief The kinds of designators that can occur in a
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -706,7 +706,7 @@
     SubstTemplateTemplateParmPacks(this_()),
     GlobalNestedNameSpecifier(0), 
     Int128Decl(0), UInt128Decl(0), Float128StubDecl(0),
-    BuiltinVaListDecl(0),
+    BuiltinVaListDecl(0), BuiltinMSVaListDecl(0),
     ObjCIdDecl(0), ObjCSelDecl(0), ObjCClassDecl(0), ObjCProtocolClassDecl(0),
     BOOLDecl(0),
     CFConstantStringTypeDecl(0), ObjCInstanceTypeDecl(0),
@@ -5692,21 +5692,30 @@
 // __builtin_va_list Construction Functions
 //===----------------------------------------------------------------------===//
 
-static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) {
-  // typedef char* __builtin_va_list;
+static TypedefDecl *CreateCharPtrNamedVaListDecl(const ASTContext *Context,
+                                                 StringRef Name) {
+  // typedef char* __builtin[_ms]_va_list;
   QualType CharPtrType = Context->getPointerType(Context->CharTy);
   TypeSourceInfo *TInfo
     = Context->getTrivialTypeSourceInfo(CharPtrType);
 
   TypedefDecl *VaListTypeDecl
     = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
                           Context->getTranslationUnitDecl(),
                           SourceLocation(), SourceLocation(),
-                          &Context->Idents.get("__builtin_va_list"),
+                          &Context->Idents.get(Name),
                           TInfo);
   return VaListTypeDecl;
 }
 
+static TypedefDecl *CreateMSVaListDecl(const ASTContext *Context) {
+  return CreateCharPtrNamedVaListDecl(Context, "__builtin_ms_va_list");
+}
+
+static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) {
+  return CreateCharPtrNamedVaListDecl(Context, "__builtin_va_list");
+}
+
 static TypedefDecl *CreateVoidPtrBuiltinVaListDecl(const ASTContext *Context) {
   // typedef void* __builtin_va_list;
   QualType VoidPtrType = Context->getPointerType(Context->VoidTy);
@@ -6141,6 +6150,13 @@
   return VaListTagTy;
 }
 
+TypedefDecl *ASTContext::getBuiltinMSVaListDecl() const {
+  if (!BuiltinMSVaListDecl)
+    BuiltinMSVaListDecl = CreateMSVaListDecl(this);
+
+  return BuiltinMSVaListDecl;
+}
+
 void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) {
   assert(ObjCConstantStringType.isNull() &&
          "'NSConstantString' type already set!");
Index: lib/AST/ASTDiagnostic.cpp
===================================================================
--- lib/AST/ASTDiagnostic.cpp
+++ lib/AST/ASTDiagnostic.cpp
@@ -73,7 +73,8 @@
       break;
 
     // Don't desugar va_list.
-    if (QualType(Ty,0) == Context.getBuiltinVaListType())
+    if (QualType(Ty,0) == Context.getBuiltinVaListType() ||
+        QualType(Ty,0) == Context.getBuiltinMSVaListType())
       break;
 
     // Otherwise, do a single-step desugar.
Index: lib/Basic/TargetInfo.cpp
===================================================================
--- lib/Basic/TargetInfo.cpp
+++ lib/Basic/TargetInfo.cpp
@@ -76,6 +76,7 @@
   RegParmMax = 0;
   SSERegParmMax = 0;
   HasAlignMac68kSupport = false;
+  HasBuiltinMSVaList = false;
 
   // Default to no types using fpret.
   RealTypeUsesObjCFPRet = 0;
Index: lib/Basic/Targets.cpp
===================================================================
--- lib/Basic/Targets.cpp
+++ lib/Basic/Targets.cpp
@@ -3107,6 +3107,9 @@
     // Use fp2ret for _Complex long double.
     ComplexLongDoubleUsesFP2Ret = true;
 
+    // Make __builtin_ms_va_list available.
+    HasBuiltinMSVaList = true;
+
     // x86-64 has atomics up to 16 bytes.
     // FIXME: Once the backend is fixed, increase MaxAtomicInlineWidth to 128
     // on CPUs with cmpxchg16b
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -193,6 +193,25 @@
   return CGF.Builder.CreateExtractValue(Tmp, 0);
 }
 
+Value *CodeGenFunction::EmitVAStartEnd(Value *ArgValue, bool IsStart) {
+  llvm::Type *DestType = Int8PtrTy;
+  if (ArgValue->getType() != DestType)
+    ArgValue = Builder.CreateBitCast(ArgValue, DestType,
+                                     ArgValue->getName().data());
+
+  Intrinsic::ID inst = IsStart ? Intrinsic::vastart : Intrinsic::vaend;
+  return Builder.CreateCall(CGM.getIntrinsic(inst), ArgValue);
+}
+
+Value *CodeGenFunction::EmitVACopy(Value *DstPtr, Value *SrcPtr) {
+  llvm::Type *Type = Int8PtrTy;
+
+  DstPtr = Builder.CreateBitCast(DstPtr, Type);
+  SrcPtr = Builder.CreateBitCast(SrcPtr, Type);
+  return Builder.CreateCall2(CGM.getIntrinsic(Intrinsic::vacopy),
+                             DstPtr, SrcPtr);
+}
+
 RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
                                         unsigned BuiltinID, const CallExpr *E) {
   // See if we can constant fold this builtin.  If so, don't emit it at all.
@@ -214,28 +233,12 @@
     return RValue::get(CGM.EmitConstantExpr(E, E->getType(), 0));
   case Builtin::BI__builtin_stdarg_start:
   case Builtin::BI__builtin_va_start:
-  case Builtin::BI__builtin_va_end: {
-    Value *ArgValue = EmitVAListRef(E->getArg(0));
-    llvm::Type *DestType = Int8PtrTy;
-    if (ArgValue->getType() != DestType)
-      ArgValue = Builder.CreateBitCast(ArgValue, DestType,
-                                       ArgValue->getName().data());
-
-    Intrinsic::ID inst = (BuiltinID == Builtin::BI__builtin_va_end) ?
-      Intrinsic::vaend : Intrinsic::vastart;
-    return RValue::get(Builder.CreateCall(CGM.getIntrinsic(inst), ArgValue));
-  }
-  case Builtin::BI__builtin_va_copy: {
-    Value *DstPtr = EmitVAListRef(E->getArg(0));
-    Value *SrcPtr = EmitVAListRef(E->getArg(1));
-
-    llvm::Type *Type = Int8PtrTy;
-
-    DstPtr = Builder.CreateBitCast(DstPtr, Type);
-    SrcPtr = Builder.CreateBitCast(SrcPtr, Type);
-    return RValue::get(Builder.CreateCall2(CGM.getIntrinsic(Intrinsic::vacopy),
-                                           DstPtr, SrcPtr));
-  }
+  case Builtin::BI__builtin_va_end:
+    return RValue::get(EmitVAStartEnd(EmitVAListRef(E->getArg(0)),
+                                     BuiltinID != Builtin::BI__builtin_va_end));
+  case Builtin::BI__builtin_va_copy:
+    return RValue::get(EmitVACopy(EmitVAListRef(E->getArg(0)),
+                                  EmitVAListRef(E->getArg(1))));
   case Builtin::BI__builtin_abs:
   case Builtin::BI__builtin_labs:
   case Builtin::BI__builtin_llabs: {
@@ -3032,6 +3035,14 @@
 
 Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
                                            const CallExpr *E) {
+  if (BuiltinID == X86::BI__builtin_ms_va_start ||
+      BuiltinID == X86::BI__builtin_ms_va_end)
+    return EmitVAStartEnd(EmitMSVAListRef(E->getArg(0)),
+                                    BuiltinID == X86::BI__builtin_ms_va_start);
+  if (BuiltinID == X86::BI__builtin_ms_va_copy)
+    return EmitVACopy(EmitMSVAListRef(E->getArg(0)),
+                      EmitMSVAListRef(E->getArg(1)));
+
   SmallVector<Value*, 4> Ops;
 
   // Find out if any arguments are required to be integer constant expressions.
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -2609,6 +2609,24 @@
 
 /* VarArg handling */
 
+llvm::Value *CodeGenFunction::EmitMSVAArg(llvm::Value *VAListAddr,
+                                          QualType Ty) {
+  llvm::Type *BPP = Int8PtrPtrTy;
+  llvm::Value *VAListAddrAsBPP = Builder.CreateBitCast(VAListAddr, BPP, "ap");
+  llvm::Value *Addr = Builder.CreateLoad(VAListAddrAsBPP, "ap.cur");
+  llvm::Type *PTy =
+    llvm::PointerType::getUnqual(ConvertType(Ty));
+  llvm::Value *AddrTyped = Builder.CreateBitCast(Addr, PTy);
+
+  uint64_t Offset =
+    llvm::RoundUpToAlignment(getContext().getTypeSize(Ty) / 8, 8);
+  llvm::Value *NextAddr =
+    Builder.CreateGEP(Addr, llvm::ConstantInt::get(Int32Ty, Offset), "ap.next");
+  Builder.CreateStore(NextAddr, VAListAddrAsBPP);
+
+  return AddrTyped;
+}
+
 llvm::Value *CodeGenFunction::EmitVAArg(llvm::Value *VAListAddr, QualType Ty) {
   return CGM.getTypes().getABIInfo().EmitVAArg(VAListAddr, Ty, *this);
 }
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -924,8 +924,12 @@
 }
 
 void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
-  llvm::Value *ArgValue = CGF.EmitVAListRef(VE->getSubExpr());
-  llvm::Value *ArgPtr = CGF.EmitVAArg(ArgValue, VE->getType());
+  llvm::Value *ArgValue = VE->isMicrosoftABI() ?
+    CGF.EmitMSVAListRef(VE->getSubExpr()) :
+    CGF.EmitVAListRef(VE->getSubExpr());
+  llvm::Value *ArgPtr = VE->isMicrosoftABI() ?
+    CGF.EmitMSVAArg(ArgValue, VE->getType()) :
+    CGF.EmitVAArg(ArgValue, VE->getType());
 
   if (!ArgPtr) {
     CGF.ErrorUnsupported(VE, "aggregate va_arg expression");
Index: lib/CodeGen/CGExprComplex.cpp
===================================================================
--- lib/CodeGen/CGExprComplex.cpp
+++ lib/CodeGen/CGExprComplex.cpp
@@ -806,8 +806,12 @@
 }
 
 ComplexPairTy ComplexExprEmitter::VisitVAArgExpr(VAArgExpr *E) {
-  llvm::Value *ArgValue = CGF.EmitVAListRef(E->getSubExpr());
-  llvm::Value *ArgPtr = CGF.EmitVAArg(ArgValue, E->getType());
+  llvm::Value *ArgValue = E->isMicrosoftABI() ?
+    CGF.EmitMSVAListRef(E->getSubExpr()) :
+    CGF.EmitVAListRef(E->getSubExpr());
+  llvm::Value *ArgPtr = E->isMicrosoftABI() ?
+    CGF.EmitMSVAArg(ArgValue, E->getType()) :
+    CGF.EmitVAArg(ArgValue, E->getType());
 
   if (!ArgPtr) {
     CGF.ErrorUnsupported(E, "complex va_arg expression");
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -3092,8 +3092,12 @@
 }
 
 Value *ScalarExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
-  llvm::Value *ArgValue = CGF.EmitVAListRef(VE->getSubExpr());
-  llvm::Value *ArgPtr = CGF.EmitVAArg(ArgValue, VE->getType());
+  llvm::Value *ArgValue = VE->isMicrosoftABI() ?
+    CGF.EmitMSVAListRef(VE->getSubExpr()) :
+    CGF.EmitVAListRef(VE->getSubExpr());
+  llvm::Value *ArgPtr = VE->isMicrosoftABI() ?
+    CGF.EmitMSVAArg(ArgValue, VE->getType()) :
+    CGF.EmitVAArg(ArgValue, VE->getType());
 
   // If EmitVAArg fails, we fall back to the LLVM instruction.
   if (!ArgPtr)
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -1376,6 +1376,10 @@
   return EmitLValue(E).getAddress();
 }
 
+llvm::Value* CodeGenFunction::EmitMSVAListRef(const Expr* E) {
+  return EmitLValue(E).getAddress();
+}
+
 void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E,
                                               llvm::Constant *Init) {
   assert (Init && "Invalid DeclRefExpr initializer!");
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1398,6 +1398,11 @@
   // or the value of the expression, depending on how va_list is defined.
   llvm::Value *EmitVAListRef(const Expr *E);
 
+  // EmitMSVAListRef - Emit a "reference" to a __builtin_ms_va_list; this is
+  // always the value of the expression, because a __builtin_ms_va_list is a
+  // pointer to a char.
+  llvm::Value *EmitMSVAListRef(const Expr *E);
+
   /// EmitAnyExprToTemp - Similary to EmitAnyExpr(), however, the result will
   /// always be accessible even if no aggregate location is provided.
   RValue EmitAnyExprToTemp(const Expr *E);
@@ -1488,6 +1493,29 @@
   /// to -1 in accordance with the Itanium C++ ABI.
   void EmitNullInitialization(llvm::Value *DestPtr, QualType Ty);
 
+  /// \brief Emits a call to an LLVM variable-argument intrinsic, either
+  /// \c llvm.va_start or \c llvm.va_end.
+  /// \param ArgValue A reference to the \c va_list as emitted by either
+  /// \c EmitVAListRef or \c EmitMSVAListRef.
+  /// \param IsStart If \c true, emits a call to \c llvm.va_start; otherwise,
+  /// calls \c llvm.va_end.
+  llvm::Value *EmitVAStartEnd(llvm::Value *ArgValue, bool IsStart);
+
+  /// \brief Emits a call to \c llvm.va_copy.
+  /// \param DstPtr A reference to the destination \c va_list as emitted by
+  /// either \c EmitVAListRef or \c EmitMSVAListRef.
+  /// \param SrcPtr A reference to the source \c va_list.
+  llvm::Value *EmitVACopy(llvm::Value *DstPtr, llvm::Value *SrcPtr);
+
+  // FIXME: We should be able to get rid of this method and use the va_arg
+  // instruction in LLVM instead once it works well enough.
+  /// \brief Generates code to get an argument from the passed-in pointer
+  /// and update it accordingly. Unlike \c EmitVAArg, this method always
+  /// obeys the Win64 ABI; it is intended to be used to generate code for
+  /// \c __builtin_va_arg when called on a \c __builtin_ms_va_list.
+  /// \return A pointer to the argument.
+  llvm::Value *EmitMSVAArg(llvm::Value *VAListAddr, QualType Ty);
+
   // EmitVAArg - Generate code to get an argument from the passed in pointer
   // and update it accordingly. The return value is a pointer to the argument.
   // FIXME: We should be able to get rid of this method and use the va_arg
Index: lib/CodeGen/TargetInfo.cpp
===================================================================
--- lib/CodeGen/TargetInfo.cpp
+++ lib/CodeGen/TargetInfo.cpp
@@ -2655,24 +2655,7 @@
 
 llvm::Value *WinX86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
                                       CodeGenFunction &CGF) const {
-  llvm::Type *BPP = CGF.Int8PtrPtrTy;
-
-  CGBuilderTy &Builder = CGF.Builder;
-  llvm::Value *VAListAddrAsBPP = Builder.CreateBitCast(VAListAddr, BPP,
-                                                       "ap");
-  llvm::Value *Addr = Builder.CreateLoad(VAListAddrAsBPP, "ap.cur");
-  llvm::Type *PTy =
-    llvm::PointerType::getUnqual(CGF.ConvertType(Ty));
-  llvm::Value *AddrTyped = Builder.CreateBitCast(Addr, PTy);
-
-  uint64_t Offset =
-    llvm::RoundUpToAlignment(CGF.getContext().getTypeSize(Ty) / 8, 8);
-  llvm::Value *NextAddr =
-    Builder.CreateGEP(Addr, llvm::ConstantInt::get(CGF.Int32Ty, Offset),
-                      "ap.next");
-  Builder.CreateStore(NextAddr, VAListAddrAsBPP);
-
-  return AddrTyped;
+  return CGF.EmitMSVAArg(VAListAddr, Ty);
 }
 
 namespace {
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -171,6 +171,12 @@
       PushOnScopeChains(Context.getObjCProtocolDecl(), TUScope);
   }
 
+  if (PP.getTargetInfo().hasBuiltinMSVaList()) {
+    DeclarationName MSVaList = &Context.Idents.get("__builtin_ms_va_list");
+    if (IdResolver.begin(MSVaList) == IdResolver.end())
+      PushOnScopeChains(Context.getBuiltinMSVaListDecl(), TUScope);
+  }
+
   DeclarationName BuiltinVaList = &Context.Idents.get("__builtin_va_list");
   if (IdResolver.begin(BuiltinVaList) == IdResolver.end())
     PushOnScopeChains(Context.getBuiltinVaListDecl(), TUScope);
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -317,6 +317,10 @@
         if (CheckMipsBuiltinFunctionCall(BuiltinID, TheCall))
           return ExprError();
         break;
+      case llvm::Triple::x86_64:
+        if (CheckX86_64BuiltinFunctionCall(BuiltinID, TheCall))
+          return ExprError();
+        break;
       default:
         break;
     }
@@ -677,6 +681,18 @@
   return false;
 }
 
+bool Sema::CheckX86_64BuiltinFunctionCall(unsigned BuiltinID,
+                                          CallExpr *TheCall) {
+  if (BuiltinID != X86::BI__builtin_ms_va_start) return false;
+
+  // FIXME: Verify that the caller is a ms_abi function.
+
+  if (SemaBuiltinVAStart(TheCall))
+    return true;
+
+  return false;
+}
+
 /// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo
 /// parameter with the FormatAttr's correct format_idx and firstDataArg.
 /// Returns true when the format fits the function and the FormatStringInfo has
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -10290,43 +10290,56 @@
                                 Expr *E, TypeSourceInfo *TInfo,
                                 SourceLocation RPLoc) {
   Expr *OrigExpr = E;
+  bool IsMS = false;
+
+  // It might be a __builtin_ms_va_list.
+  if (!E->isTypeDependent() &&
+      Context.getTargetInfo().hasBuiltinMSVaList()) {
+    QualType MSVaListType = Context.getBuiltinMSVaListType();
+    if (Context.hasSameType(MSVaListType, E->getType())) {
+      if (CheckForModifiableLvalue(E, BuiltinLoc, *this))
+        return ExprError();
+      IsMS = true;
+    }
+  }
 
   // Get the va_list type
   QualType VaListType = Context.getBuiltinVaListType();
-  if (VaListType->isArrayType()) {
-    // Deal with implicit array decay; for example, on x86-64,
-    // va_list is an array, but it's supposed to decay to
-    // a pointer for va_arg.
-    VaListType = Context.getArrayDecayedType(VaListType);
-    // Make sure the input expression also decays appropriately.
-    ExprResult Result = UsualUnaryConversions(E);
-    if (Result.isInvalid())
-      return ExprError();
-    E = Result.take();
-  } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) {
-    // If va_list is a record type and we are compiling in C++ mode,
-    // check the argument using reference binding.
-    InitializedEntity Entity
-      = InitializedEntity::InitializeParameter(Context,
-          Context.getLValueReferenceType(VaListType), false);
-    ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E);
-    if (Init.isInvalid())
-      return ExprError();
-    E = Init.takeAs<Expr>();
-  } else {
-    // Otherwise, the va_list argument must be an l-value because
-    // it is modified by va_arg.
-    if (!E->isTypeDependent() &&
-        CheckForModifiableLvalue(E, BuiltinLoc, *this))
-      return ExprError();
+  if (!IsMS) {
+    if (VaListType->isArrayType()) {
+      // Deal with implicit array decay; for example, on x86-64,
+      // va_list is an array, but it's supposed to decay to
+      // a pointer for va_arg.
+      VaListType = Context.getArrayDecayedType(VaListType);
+      // Make sure the input expression also decays appropriately.
+      ExprResult Result = UsualUnaryConversions(E);
+      if (Result.isInvalid())
+        return ExprError();
+      E = Result.take();
+    } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) {
+      // If va_list is a record type and we are compiling in C++ mode,
+      // check the argument using reference binding.
+      InitializedEntity Entity
+        = InitializedEntity::InitializeParameter(Context,
+            Context.getLValueReferenceType(VaListType), false);
+      ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E);
+      if (Init.isInvalid())
+        return ExprError();
+      E = Init.takeAs<Expr>();
+    } else {
+      // Otherwise, the va_list argument must be an l-value because
+      // it is modified by va_arg.
+      if (!E->isTypeDependent() &&
+          CheckForModifiableLvalue(E, BuiltinLoc, *this))
+        return ExprError();
+    }
   }
 
-  if (!E->isTypeDependent() &&
-      !Context.hasSameType(VaListType, E->getType())) {
+  if (!IsMS && !E->isTypeDependent() &&
+      !Context.hasSameType(VaListType, E->getType()))
     return ExprError(Diag(E->getLocStart(),
                          diag::err_first_argument_to_va_arg_not_of_type_va_list)
       << OrigExpr->getType() << E->getSourceRange());
-  }
 
   if (!TInfo->getType()->isDependentType()) {
     if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(),
@@ -10368,7 +10381,7 @@
   }
 
   QualType T = TInfo->getType().getNonLValueExprType(Context);
-  return Owned(new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T));
+  return Owned(new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS));
 }
 
 ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) {
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp
+++ lib/Serialization/ASTReader.cpp
@@ -5485,6 +5485,9 @@
 
     case PREDEF_DECL_BUILTIN_VA_LIST_ID:
       return Context.getBuiltinVaListDecl();
+
+    case PREDEF_DECL_BUILTIN_MS_VA_LIST_ID:
+      return Context.getBuiltinMSVaListDecl();
     }
   }
   
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -2448,6 +2448,10 @@
                                          NumArrayIndexVars);
       break;
     }
+
+    case EXPR_MS_VA_ARG:
+      S = new (Context) VAArgExpr(Empty, true);
+      break;
     }
     
     // We hit a STMT_STOP, so we're done with this expression.
Index: lib/Serialization/ASTWriter.cpp
===================================================================
--- lib/Serialization/ASTWriter.cpp
+++ lib/Serialization/ASTWriter.cpp
@@ -772,6 +772,7 @@
   RECORD(EXPR_SIZEOF_PACK);
   RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK);
   RECORD(EXPR_CUDA_KERNEL_CALL);
+  RECORD(EXPR_MS_VA_ARG);
 #undef RECORD
 }
 
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -748,7 +748,8 @@
   Writer.AddTypeSourceInfo(E->getWrittenTypeInfo(), Record);
   Writer.AddSourceLocation(E->getBuiltinLoc(), Record);
   Writer.AddSourceLocation(E->getRParenLoc(), Record);
-  Code = serialization::EXPR_VA_ARG;
+  Code = E->isMicrosoftABI() ? serialization::EXPR_MS_VA_ARG :
+                               serialization::EXPR_VA_ARG;
 }
 
 void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) {
Index: test/CodeGen/ms_abi.c
===================================================================
--- test/CodeGen/ms_abi.c
+++ test/CodeGen/ms_abi.c
@@ -1,11 +1,19 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 -emit-llvm < %s | FileCheck -check-prefix=FREEBSD %s
 // RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm < %s | FileCheck -check-prefix=WIN64 %s
 
+struct foo {
+  int x;
+  float y;
+  char z;
+};
+// FREEBSD: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
+// WIN64: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
+
 void __attribute__((ms_abi)) f1(void);
 void __attribute__((sysv_abi)) f2(void);
 void f3(void) {
-// FREEBSD: define void @f3()
-// WIN64: define void @f3()
+// FREEBSD-LABEL: define void @f3()
+// WIN64-LABEL: define void @f3()
   f1();
 // FREEBSD: call x86_64_win64cc void @f1()
 // WIN64: call void @f1()
@@ -18,3 +26,77 @@
 // WIN64: declare void @f1()
 // WIN64: declare x86_64_sysvcc void @f2()
 
+// Win64 ABI varargs
+void __attribute__((ms_abi)) f4(int a, ...) {
+// FREEBSD-LABEL: define x86_64_win64cc void @f4
+// WIN64-LABEL: define void @f4
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  // FREEBSD: %[[AP_S:.*]] = bitcast i8** %[[AP:.*]] to i8*
+  // FREEBSD-NEXT: call void @llvm.va_start(i8* %[[AP_S]]
+  // WIN64: %[[AP_S:.*]] = bitcast i8** %[[AP:.*]] to i8*
+  // WIN64-NEXT: call void @llvm.va_start(i8* %[[AP_S]]
+  int b = __builtin_va_arg(ap, int);
+  // FREEBSD: %[[AP_CUR:.*]] = load i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr i8* %[[AP_CUR]], i32 8
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // WIN64: %[[AP_CUR:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr i8* %[[AP_CUR]], i32 8
+  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  double _Complex c = __builtin_va_arg(ap, double _Complex);
+  // FREEBSD: %[[AP_CUR2:.*]] = load i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr i8* %[[AP_CUR2]], i32 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // WIN64: %[[AP_CUR2:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr i8* %[[AP_CUR2]], i32 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  struct foo d = __builtin_va_arg(ap, struct foo);
+  // FREEBSD: %[[AP_CUR3:.*]] = load i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr i8* %[[AP_CUR3]], i32 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // WIN64: %[[AP_CUR3:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr i8* %[[AP_CUR3]], i32 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  __builtin_ms_va_list ap2;
+  __builtin_ms_va_copy(ap2, ap);
+  // FREEBSD: call void @llvm.va_copy
+  // WIN64: call void @llvm.va_copy
+  __builtin_ms_va_end(ap);
+  // FREEBSD: call void @llvm.va_end
+  // WIN64: call void @llvm.va_end
+}
+
+// Let's verify that normal va_lists work right on Win64, too.
+void f5(int a, ...) {
+// WIN64-LABEL: define void @f5
+  __builtin_va_list ap;
+  __builtin_va_start(ap, a);
+  // WIN64: %[[AP_S:.*]] = bitcast i8** %[[AP:.*]] to i8*
+  // WIN64-NEXT: call void @llvm.va_start(i8* %[[AP_S]]
+  int b = __builtin_va_arg(ap, int);
+  // WIN64: %[[AP_CUR:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr i8* %[[AP_CUR]], i32 8
+  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  double _Complex c = __builtin_va_arg(ap, double _Complex);
+  // WIN64: %[[AP_CUR2:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr i8* %[[AP_CUR2]], i32 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  struct foo d = __builtin_va_arg(ap, struct foo);
+  // WIN64: %[[AP_CUR3:.*]] = load i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr i8* %[[AP_CUR3]], i32 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  __builtin_va_list ap2;
+  __builtin_va_copy(ap2, ap);
+  // WIN64: call void @llvm.va_copy
+  __builtin_va_end(ap);
+  // WIN64: call void @llvm.va_end
+}
Index: test/PCH/Inputs/va_arg.h
===================================================================
--- test/PCH/Inputs/va_arg.h
+++ test/PCH/Inputs/va_arg.h
@@ -1,2 +1,5 @@
 #include <stdarg.h>
 
+typedef __builtin_ms_va_list __ms_va_list;
+#define __ms_va_start(ap,a) __builtin_ms_va_start(ap,a)
+#define __ms_va_end(ap) __builtin_ms_va_end(ap)
Index: test/PCH/va_arg.c
===================================================================
--- test/PCH/va_arg.c
+++ test/PCH/va_arg.c
@@ -10,3 +10,10 @@
 char *g(char **argv) {
   f(g0, argv, 1, 2, 3);
 }
+
+char *i0(char** argv, int argc) { return argv[argc]; }
+
+char *i(char **argv) {
+  h(i0, argv, 1, 2, 3);
+}
+
Index: test/PCH/va_arg.cpp
===================================================================
--- test/PCH/va_arg.cpp
+++ test/PCH/va_arg.cpp
@@ -9,8 +9,13 @@
 
 extern "C" {
 int vsnprintf(char * , size_t, const char * , va_list) ;
+int __attribute__((ms_abi)) wvsprintfA(char *, const char *, __ms_va_list);
 }
 
 void f(char *buffer, unsigned count, const char* format, va_list argptr) {
   vsnprintf(buffer, count, format, argptr);
 }
+
+void g(char *buffer, const char* format, __ms_va_list argptr) {
+  wvsprintfA(buffer, format, argptr);
+}
Index: test/PCH/va_arg.h
===================================================================
--- test/PCH/va_arg.h
+++ test/PCH/va_arg.h
@@ -6,3 +6,10 @@
     va_list v;
     s = g (p, __builtin_va_arg(v, int));
 }
+
+typedef __builtin_ms_va_list __ms_va_list;
+char * __attribute__((ms_abi)) h (char * (*i) (char **, int), char **p, ...) {
+    char *s;
+    __ms_va_list v;
+    s = i (p, __builtin_va_arg(v, int));
+}
Index: test/Sema/varargs-x86-64.c
===================================================================
--- test/Sema/varargs-x86-64.c
+++ test/Sema/varargs-x86-64.c
@@ -6,3 +6,70 @@
   (void)__builtin_va_arg(args2, int); // expected-error {{first argument to 'va_arg' is of type 'const __builtin_va_list' and not 'va_list'}}
 }
 
+void __attribute__((ms_abi)) g1(int a)
+{
+    __builtin_ms_va_list ap;
+
+    __builtin_ms_va_start(ap, a, a); // expected-error {{too many arguments to function}}
+    __builtin_ms_va_start(ap, a); // expected-error {{'va_start' used in function with fixed args}}
+}
+
+void __attribute__((ms_abi)) g2(int a, int b, ...)
+{
+    __builtin_ms_va_list ap;
+
+    __builtin_ms_va_start(ap, 10); // expected-warning {{second parameter of 'va_start' not last named argument}}
+    __builtin_ms_va_start(ap, a); // expected-warning {{second parameter of 'va_start' not last named argument}}
+    __builtin_ms_va_start(ap, b);
+}
+
+void __attribute__((ms_abi)) g3(float a, ...)
+{
+    __builtin_ms_va_list ap;
+
+    __builtin_ms_va_start(ap, a);
+    __builtin_ms_va_start(ap, (a));
+}
+
+void __attribute__((ms_abi)) g5() {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap,ap); // expected-error {{'va_start' used in function with fixed args}}
+}
+
+void __attribute__((ms_abi)) g6(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap); // expected-error {{too few arguments to function}}
+}
+
+void __attribute__((ms_abi))
+bar(__builtin_ms_va_list authors, ...) {
+  __builtin_ms_va_start (authors, authors);
+  (void)__builtin_va_arg(authors, int);
+  __builtin_ms_va_end (authors);
+}
+
+void __attribute__((ms_abi)) g7(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  // FIXME: This error message is sub-par.
+  __builtin_va_arg(ap, int) = 1; // expected-error {{expression is not assignable}}
+  int *x = &__builtin_va_arg(ap, int); // expected-error {{cannot take the address of an rvalue}}
+  __builtin_ms_va_end(ap);
+}
+
+void __attribute__((ms_abi)) g8(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  (void)__builtin_va_arg(ap, void); // expected-error {{second argument to 'va_arg' is of incomplete type 'void'}}
+  __builtin_ms_va_end(ap);
+}
+
+enum E { x = -1, y = 2, z = 10000 };
+void __attribute__((ms_abi)) g9(__builtin_ms_va_list args)
+{
+    (void)__builtin_va_arg(args, float); // expected-warning {{second argument to 'va_arg' is of promotable type 'float'}}
+    (void)__builtin_va_arg(args, enum E); // Don't warn here in C
+    (void)__builtin_va_arg(args, short); // expected-warning {{second argument to 'va_arg' is of promotable type 'short'}}
+    (void)__builtin_va_arg(args, char); // expected-warning {{second argument to 'va_arg' is of promotable type 'char'}}
+}
+
Index: test/SemaTemplate/instantiate-expr-3.cpp
===================================================================
--- test/SemaTemplate/instantiate-expr-3.cpp
+++ test/SemaTemplate/instantiate-expr-3.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
 
 // ---------------------------------------------------------------------
 // Imaginary literals
@@ -108,12 +108,41 @@
 struct VaArg1 {
   void f(int n, ...) {
     VaList va;
-    __builtin_va_start(va, n); // expected-error{{int}}
+    __builtin_va_start(va, n); // expected-error{{int}} expected-error{{char *}}
     for (int i = 0; i != n; ++i)
       (void)__builtin_va_arg(va, ArgType); // expected-error{{int}}
-    __builtin_va_end(va); // expected-error{{int}}
+    __builtin_va_end(va); // expected-error{{int}} expected-error{{char *}}
   }
 };
 
 template struct VaArg1<__builtin_va_list, int>;
+template struct VaArg1<__builtin_ms_va_list, int>; // expected-note{{instantiation}}
 template struct VaArg1<int, int>; // expected-note{{instantiation}}
+
+template<typename ArgType>
+struct VaArg2 {
+  void f(int n, ...) {
+    __builtin_ms_va_list va;
+    __builtin_ms_va_start(va, n);
+    for (int i = 0; i != n; ++i)
+      (void)__builtin_va_arg(va, ArgType);
+    __builtin_ms_va_end(va);
+  }
+};
+
+template struct VaArg2<int>;
+
+template<typename VaList, typename ArgType>
+struct VaArg3 {
+  void f(int n, ...) {
+    VaList va;
+    __builtin_ms_va_start(va, n); // expected-error{{int}} expected-error{{__va_list_tag}}
+    for (int i = 0; i != n; ++i)
+      (void)__builtin_va_arg(va, ArgType); // expected-error{{int}}
+    __builtin_ms_va_end(va); // expected-error{{int}} expected-error{{__va_list_tag}}
+  }
+};
+
+template struct VaArg3<__builtin_ms_va_list, int>;
+template struct VaArg3<__builtin_va_list, int>; // expected-note{{instantiation}}
+template struct VaArg3<int, int>; // expected-note{{instantiation}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to