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 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(Val); } Expr *getSubExpr() { return cast(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 __builtin_ms_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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6312,6 +6312,8 @@ def warn_second_parameter_to_va_arg_never_compatible : Warning< "second argument to 'va_arg' is of promotable type %0; this va_arg has " "undefined behavior because arguments will be promoted to %1">, InGroup; +def err_ms_va_list_not_supported : Error< + "builtin function '__builtin_ms_va_arg' not supported for this target">; def warn_return_missing_expr : Warning< "non-void %select{function|method}1 %0 should return a value">, DefaultError, 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/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -336,6 +336,7 @@ KEYWORD(__builtin_offsetof , KEYALL) KEYWORD(__builtin_types_compatible_p, KEYALL) KEYWORD(__builtin_va_arg , KEYALL) +KEYWORD(__builtin_ms_va_arg , KEYALL) KEYWORD(__extension__ , KEYALL) KEYWORD(__imag , KEYALL) KEYWORD(__int128 , KEYALL) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3488,6 +3488,12 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + // __builtin_ms_va_arg(expr, type) + ExprResult ActOnMSVAArg(SourceLocation BuiltinLoc, Expr *E, ParsedType Ty, + SourceLocation RPLoc); + ExprResult BuildMSVAArgExpr(SourceLocation BuiltinLoc, Expr *E, + TypeSourceInfo *TInfo, SourceLocation RPLoc); + // __null ExprResult ActOnGNUNullExpr(SourceLocation TokenLoc); @@ -7681,6 +7687,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,8 +5692,9 @@ // __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); @@ -5702,11 +5703,19 @@ = TypedefDecl::Create(const_cast(*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/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1157,7 +1157,7 @@ } void StmtPrinter::VisitVAArgExpr(VAArgExpr *Node) { - OS << "__builtin_va_arg("; + OS << (Node->isMicrosoftABI() ? "__builtin_ms_va_arg(" : "__builtin_va_arg("); PrintExpr(Node->getSubExpr()); OS << ", "; Node->getType().print(OS, Policy); 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: { @@ -3016,6 +3019,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 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.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_ms_va_arg. + /// \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/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/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/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -494,6 +494,7 @@ /// [GNU] '__PRETTY_FUNCTION__' /// [GNU] '(' compound-statement ')' /// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')' +/// [GNU] '__builtin_ms_va_arg' '(' assignment-expression ',' type-name ')' /// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' @@ -885,6 +886,7 @@ Res = ParseGenericSelectionExpression(); break; case tok::kw___builtin_va_arg: + case tok::kw___builtin_ms_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: case tok::kw___builtin_astype: // primary-expression: [OCL] as_type() @@ -1733,6 +1735,7 @@ /// \verbatim /// primary-expression: [C99 6.5.1] /// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')' +/// [GNU] '__builtin_ms_va_arg' '(' assignment-expression ',' type-name ')' /// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' @@ -1763,7 +1766,8 @@ switch (T) { default: llvm_unreachable("Not a builtin primary expression!"); - case tok::kw___builtin_va_arg: { + case tok::kw___builtin_va_arg: + case tok::kw___builtin_ms_va_arg: { ExprResult Expr(ParseAssignmentExpression()); if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) @@ -1778,6 +1782,8 @@ if (Expr.isInvalid() || Ty.isInvalid()) Res = ExprError(); + else if (T == tok::kw___builtin_ms_va_arg) + Res = Actions.ActOnMSVAArg(StartLoc,Expr.take(),Ty.get(),ConsumeParen()); else Res = Actions.ActOnVAArg(StartLoc, Expr.take(), Ty.get(), ConsumeParen()); break; Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -777,6 +777,7 @@ case tok::kw___builtin_offsetof: case tok::kw___builtin_types_compatible_p: case tok::kw___builtin_va_arg: + case tok::kw___builtin_ms_va_arg: case tok::kw___imag: case tok::kw___real: case tok::kw___FUNCTION__: 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 @@ -10286,9 +10286,77 @@ return BuildVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); } -ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, - Expr *E, TypeSourceInfo *TInfo, - SourceLocation RPLoc) { +ExprResult Sema::ActOnMSVAArg(SourceLocation BuiltinLoc, Expr *E, + ParsedType Ty, SourceLocation RPLoc) { + if (!Context.getTargetInfo().hasBuiltinMSVaList()) + return ExprError(Diag(BuiltinLoc, diag::err_ms_va_list_not_supported)); + TypeSourceInfo *TInfo; + GetTypeFromParser(Ty, &TInfo); + return BuildMSVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); +} + +static bool VerifyPtrVAArg(Sema &S, SourceLocation BuiltinLoc, Expr *E) { + return !E->isTypeDependent() && + CheckForModifiableLvalue(E, BuiltinLoc, S); +} + +static ExprResult BuildAnyVAArgExpr(Sema &S, SourceLocation BuiltinLoc, + Expr *OrigExpr, Expr *E, + QualType VaListType, TypeSourceInfo *TInfo, + SourceLocation RPLoc) { + if (!E->isTypeDependent() && + !S.Context.hasSameType(VaListType, E->getType())) { + return ExprError(S.Diag(E->getLocStart(), + diag::err_first_argument_to_va_arg_not_of_type_va_list) + << OrigExpr->getType() << E->getSourceRange()); + } + + if (!TInfo->getType()->isDependentType()) { + if (S.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), + diag::err_second_parameter_to_va_arg_incomplete, + TInfo->getTypeLoc())) + return ExprError(); + + if (S.RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), + diag::err_second_parameter_to_va_arg_abstract, + TInfo->getTypeLoc())) + return ExprError(); + + if (!TInfo->getType().isPODType(S.Context)) { + S.Diag(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType()->isObjCLifetimeType() + ? diag::warn_second_parameter_to_va_arg_ownership_qualified + : diag::warn_second_parameter_to_va_arg_not_pod) + << TInfo->getType() + << TInfo->getTypeLoc().getSourceRange(); + } + + // Check for va_arg where arguments of the given type will be promoted + // (i.e. this va_arg is guaranteed to have undefined behavior). + QualType PromoteType; + if (TInfo->getType()->isPromotableIntegerType()) { + PromoteType = S.Context.getPromotedIntegerType(TInfo->getType()); + if (S.Context.typesAreCompatible(PromoteType, TInfo->getType())) + PromoteType = QualType(); + } + if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) + PromoteType = S.Context.DoubleTy; + if (!PromoteType.isNull()) + S.DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, + S.PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) + << TInfo->getType() + << PromoteType + << TInfo->getTypeLoc().getSourceRange()); + } + + QualType T = TInfo->getType().getNonLValueExprType(S.Context); + return S.Owned(new (S.Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T)); +} + +ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, + TypeSourceInfo *TInfo, SourceLocation RPLoc) { Expr *OrigExpr = E; // Get the va_list type @@ -10316,59 +10384,29 @@ } 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)) + if (VerifyPtrVAArg(*this, BuiltinLoc, E)) return ExprError(); } - if (!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(), - diag::err_second_parameter_to_va_arg_incomplete, - TInfo->getTypeLoc())) - return ExprError(); - - if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType(), - diag::err_second_parameter_to_va_arg_abstract, - TInfo->getTypeLoc())) - return ExprError(); + return BuildAnyVAArgExpr(*this, BuiltinLoc, OrigExpr, E, VaListType, TInfo, + RPLoc); +} - if (!TInfo->getType().isPODType(Context)) { - Diag(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType()->isObjCLifetimeType() - ? diag::warn_second_parameter_to_va_arg_ownership_qualified - : diag::warn_second_parameter_to_va_arg_not_pod) - << TInfo->getType() - << TInfo->getTypeLoc().getSourceRange(); - } +ExprResult Sema::BuildMSVAArgExpr(SourceLocation BuiltinLoc, Expr *E, + TypeSourceInfo *TInfo, + SourceLocation RPLoc) { + QualType VaListType = Context.getBuiltinMSVaListType(); + // The va_list argument must be an l-value because it is modified by va_arg. + if (VerifyPtrVAArg(*this, BuiltinLoc, E)) + return ExprError(); - // Check for va_arg where arguments of the given type will be promoted - // (i.e. this va_arg is guaranteed to have undefined behavior). - QualType PromoteType; - if (TInfo->getType()->isPromotableIntegerType()) { - PromoteType = Context.getPromotedIntegerType(TInfo->getType()); - if (Context.typesAreCompatible(PromoteType, TInfo->getType())) - PromoteType = QualType(); - } - if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) - PromoteType = Context.DoubleTy; - if (!PromoteType.isNull()) - DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, - PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) - << TInfo->getType() - << PromoteType - << TInfo->getTypeLoc().getSourceRange()); + ExprResult Res = BuildAnyVAArgExpr(*this, BuiltinLoc, E, E, VaListType, + TInfo, RPLoc); + if (VAArgExpr *E = Res.takeAs()) { + E->setIsMicrosoftABI(true); + return Owned(E); } - - QualType T = TInfo->getType().getNonLValueExprType(Context); - return Owned(new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T)); + return Res; } ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -1780,6 +1780,18 @@ RParenLoc); } + /// \brief Build a new \c __builtin_ms_va_arg expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildMSVAArgExpr(SourceLocation BuiltinLoc, + Expr *SubExpr, TypeSourceInfo *TInfo, + SourceLocation RParenLoc) { + return getSema().BuildMSVAArgExpr(BuiltinLoc, + SubExpr, TInfo, + RParenLoc); + } + /// \brief Build a new expression list in parentheses. /// /// By default, performs semantic analysis to build the new expression. @@ -7073,8 +7085,11 @@ SubExpr.get() == E->getSubExpr()) return SemaRef.Owned(E); - return getDerived().RebuildVAArgExpr(E->getBuiltinLoc(), SubExpr.get(), - TInfo, E->getRParenLoc()); + return E->isMicrosoftABI() ? + getDerived().RebuildMSVAArgExpr(E->getBuiltinLoc(), SubExpr.get(), + TInfo, E->getRParenLoc()) : + getDerived().RebuildVAArgExpr(E->getBuiltinLoc(), SubExpr.get(), + TInfo, E->getRParenLoc()); } template 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_ms_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_ms_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_ms_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/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_ms_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_ms_va_arg(ap, int) = 1; // expected-error {{expression is not assignable}} + int *x = &__builtin_ms_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_ms_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_ms_va_arg(args, float); // expected-warning {{second argument to 'va_arg' is of promotable type 'float'}} + (void)__builtin_ms_va_arg(args, enum E); // Don't warn here in C + (void)__builtin_ms_va_arg(args, short); // expected-warning {{second argument to 'va_arg' is of promotable type 'short'}} + (void)__builtin_ms_va_arg(args, char); // expected-warning {{second argument to 'va_arg' is of promotable type 'char'}} +} +