From: Charles Davis Date: Thu, 17 Sep 2015 20:55:33 +0000 (+0000) Subject: Support __builtin_ms_va_list. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=69b5694b76d36fde04c1f3511459a575e48f201d;p=clang Support __builtin_ms_va_list. Summary: This change adds support for `__builtin_ms_va_list`, a GCC extension for variadic `ms_abi` functions. The existing `__builtin_va_list` support is inadequate for this because `va_list` is defined differently in the Win64 ABI vs. the System V/AMD64 ABI. Depends on D1622. Reviewers: rsmith, rnk, rjmccall CC: cfe-commits Differential Revision: http://reviews.llvm.org/D1623 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247941 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 7f6bc678c1..b5d03e5e37 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -216,6 +216,9 @@ class ASTContext : public RefCountedBase { /// __builtin_va_list type. mutable TypedefDecl *BuiltinVaListDecl; + /// 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; @@ -1579,6 +1582,15 @@ public: /// for some targets. Decl *getVaListTagDecl() const; + /// Retrieve the C type declaration corresponding to the predefined + /// \c __builtin_ms_va_list type. + TypedefDecl *getBuiltinMSVaListDecl() const; + + /// 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 { diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index e44ce01706..4ea3e30a9a 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -3699,33 +3699,35 @@ public: } }; -/// VAArgExpr, used for the builtin function __builtin_va_arg. +/// Represents a call to the builtin function \c __builtin_va_arg. class VAArgExpr : public Expr { Stmt *Val; - TypeSourceInfo *TInfo; + llvm::PointerIntPair TInfo; SourceLocation BuiltinLoc, RParenLoc; public: - VAArgExpr(SourceLocation BLoc, Expr* e, TypeSourceInfo *TInfo, - SourceLocation RPLoc, QualType t) - : Expr(VAArgExprClass, t, VK_RValue, OK_Ordinary, - t->isDependentType(), false, - (TInfo->getType()->isInstantiationDependentType() || - e->isInstantiationDependent()), - (TInfo->getType()->containsUnexpandedParameterPack() || - e->containsUnexpandedParameterPack())), - Val(e), TInfo(TInfo), - BuiltinLoc(BLoc), - RParenLoc(RPLoc) { } - - /// \brief Create an empty __builtin_va_arg expression. - explicit VAArgExpr(EmptyShell Empty) : Expr(VAArgExprClass, Empty) { } + VAArgExpr(SourceLocation BLoc, Expr *e, TypeSourceInfo *TInfo, + SourceLocation RPLoc, QualType t, bool IsMS) + : Expr(VAArgExprClass, t, VK_RValue, OK_Ordinary, t->isDependentType(), + false, (TInfo->getType()->isInstantiationDependentType() || + e->isInstantiationDependent()), + (TInfo->getType()->containsUnexpandedParameterPack() || + e->containsUnexpandedParameterPack())), + Val(e), TInfo(TInfo, IsMS), BuiltinLoc(BLoc), RParenLoc(RPLoc) {} + + /// Create an empty __builtin_va_arg expression. + explicit VAArgExpr(EmptyShell Empty) + : Expr(VAArgExprClass, Empty), Val(0), TInfo(0, false) {} 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; } + /// 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; } diff --git a/include/clang/Basic/BuiltinsX86.def b/include/clang/Basic/BuiltinsX86.def index 73e146e8aa..f0389596c1 100644 --- a/include/clang/Basic/BuiltinsX86.def +++ b/include/clang/Basic/BuiltinsX86.def @@ -30,6 +30,11 @@ // can use it? BUILTIN(__builtin_cpu_supports, "bcC*", "nc") +// Win64-compatible va_list functions +BUILTIN(__builtin_ms_va_start, "vc*&.", "nt") +BUILTIN(__builtin_ms_va_end, "vc*&", "n") +BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") + // Undefined Values // TARGET_BUILTIN(__builtin_ia32_undef128, "V2d", "nc", "") diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9565f67d3e..7d22327961 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7102,6 +7102,10 @@ def note_empty_body_on_separate_line : Note< def err_va_start_used_in_non_variadic_function : Error< "'va_start' used in function with fixed args">; +def err_va_start_used_in_wrong_abi_function : Error< + "'va_start' used in %select{System V|Win64}0 ABI function">; +def err_ms_va_start_used_in_sysv_function : Error< + "'__builtin_ms_va_start' used in System V ABI function">; def warn_second_parameter_of_va_start_not_last_named_argument : Warning< "second parameter of 'va_start' not last named argument">, InGroup; def warn_va_start_of_reference_type_is_undefined : Warning< @@ -7216,6 +7220,8 @@ def err_64_bit_builtin_32_bit_tgt : Error< "this builtin is only available on 64-bit targets">; def err_ppc_builtin_only_on_pwr7 : Error< "this builtin is only valid on POWER7 or later CPUs">; +def err_x86_builtin_32_bit_tgt : Error< + "this builtin is only available on x86-64 targets">; def err_builtin_longjmp_unsupported : Error< "__builtin_longjmp is not supported for the current target">; diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 4683ede63d..638b6790f7 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -90,6 +90,8 @@ protected: unsigned RealTypeUsesObjCFPRet : 3; unsigned ComplexLongDoubleUsesFP2Ret : 1; + unsigned HasBuiltinMSVaList : 1; + // TargetInfo Constructor. Default initializes all fields. TargetInfo(const llvm::Triple &T); @@ -526,6 +528,10 @@ public: /// with this target. virtual BuiltinVaListKind getBuiltinVaListKind() const = 0; + /// 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. /// diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 57bf65e7ce..273c729a8c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -8832,8 +8832,10 @@ private: bool CheckSystemZBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckPPCBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); - + + bool SemaBuiltinVAStartImpl(CallExpr *TheCall); bool SemaBuiltinVAStart(CallExpr *TheCall); + bool SemaBuiltinMSVAStart(CallExpr *TheCall); bool SemaBuiltinVAStartARM(CallExpr *Call); bool SemaBuiltinUnorderedCompare(CallExpr *TheCall); bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 0dec07f949..5d0669f4bf 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -933,29 +933,29 @@ namespace clang { /// it is created. enum PredefinedDeclIDs { /// \brief The NULL declaration. - PREDEF_DECL_NULL_ID = 0, - + PREDEF_DECL_NULL_ID = 0, + /// \brief The translation unit. PREDEF_DECL_TRANSLATION_UNIT_ID = 1, - + /// \brief The Objective-C 'id' type. PREDEF_DECL_OBJC_ID_ID = 2, - + /// \brief The Objective-C 'SEL' type. PREDEF_DECL_OBJC_SEL_ID = 3, - + /// \brief The Objective-C 'Class' type. PREDEF_DECL_OBJC_CLASS_ID = 4, - + /// \brief The Objective-C 'Protocol' type. PREDEF_DECL_OBJC_PROTOCOL_ID = 5, - + /// \brief The signed 128-bit integer type. PREDEF_DECL_INT_128_ID = 6, /// \brief The unsigned 128-bit integer type. PREDEF_DECL_UNSIGNED_INT_128_ID = 7, - + /// \brief The internal 'instancetype' typedef. PREDEF_DECL_OBJC_INSTANCETYPE_ID = 8, @@ -965,15 +965,18 @@ namespace clang { /// \brief The internal '__va_list_tag' struct, if any. PREDEF_DECL_VA_LIST_TAG = 10, + /// \brief The internal '__builtin_ms_va_list' typedef. + PREDEF_DECL_BUILTIN_MS_VA_LIST_ID = 11, + /// \brief The extern "C" context. - PREDEF_DECL_EXTERN_C_CONTEXT_ID = 11, + PREDEF_DECL_EXTERN_C_CONTEXT_ID = 12, }; /// \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 = 12; + const unsigned int NUM_PREDEF_DECL_IDS = 13; /// \brief Record code for a list of local redeclarations of a declaration. const unsigned int LOCAL_REDECLARATIONS = 50; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e5511cbd61..3e0d6d1144 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -733,8 +733,9 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, SubstTemplateTemplateParmPacks(this_()), GlobalNestedNameSpecifier(nullptr), Int128Decl(nullptr), UInt128Decl(nullptr), Float128StubDecl(nullptr), - BuiltinVaListDecl(nullptr), ObjCIdDecl(nullptr), ObjCSelDecl(nullptr), - ObjCClassDecl(nullptr), ObjCProtocolClassDecl(nullptr), BOOLDecl(nullptr), + BuiltinVaListDecl(nullptr), BuiltinMSVaListDecl(nullptr), + ObjCIdDecl(nullptr), ObjCSelDecl(nullptr), ObjCClassDecl(nullptr), + ObjCProtocolClassDecl(nullptr), BOOLDecl(nullptr), CFConstantStringTypeDecl(nullptr), ObjCInstanceTypeDecl(nullptr), FILEDecl(nullptr), jmp_bufDecl(nullptr), sigjmp_bufDecl(nullptr), ucontext_tDecl(nullptr), BlockDescriptorType(nullptr), @@ -6039,10 +6040,19 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const { // __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 T = Context->getPointerType(Context->CharTy); - return Context->buildImplicitTypedef(T, "__builtin_va_list"); + return Context->buildImplicitTypedef(T, Name); +} + +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) { @@ -6378,6 +6388,13 @@ Decl *ASTContext::getVaListTagDecl() const { return VaListTagDecl; } +TypedefDecl *ASTContext::getBuiltinMSVaListDecl() const { + if (!BuiltinMSVaListDecl) + BuiltinMSVaListDecl = CreateMSVaListDecl(this); + + return BuiltinMSVaListDecl; +} + void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) { assert(ObjCConstantStringType.isNull() && "'NSConstantString' type already set!"); diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 0544ccbef7..0ab1fa7886 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -132,7 +132,8 @@ static QualType Desugar(ASTContext &Context, QualType QT, bool &ShouldAKA) { 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. diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index aa7385eb00..7b52eb0f08 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -77,6 +77,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) { RegParmMax = 0; SSERegParmMax = 0; HasAlignMac68kSupport = false; + HasBuiltinMSVaList = false; // Default to no types using fpret. RealTypeUsesObjCFPRet = 0; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 35d7030a25..b969f2a079 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -3894,6 +3894,9 @@ public: // Use fp2ret for _Complex long double. ComplexLongDoubleUsesFP2Ret = true; + // Make __builtin_ms_va_list available. + HasBuiltinMSVaList = true; + // x86-64 has atomics up to 16 bytes. MaxAtomicPromoteWidth = 128; MaxAtomicInlineWidth = 128; diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index ece36e8879..a65f270856 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -85,6 +85,12 @@ namespace clang { CodeGen::Address VAListAddr, QualType Ty) const = 0; + /// Emit the target dependent code to load a value of + /// \arg Ty from the \c __builtin_ms_va_list pointed to by \arg VAListAddr. + virtual CodeGen::Address EmitMSVAArg(CodeGen::CodeGenFunction &CGF, + CodeGen::Address VAListAddr, + QualType Ty) const; + virtual bool isHomogeneousAggregateBaseType(QualType Ty) const; virtual bool isHomogeneousAggregateSmallEnough(const Type *Base, diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 0cad469f78..91614a5a64 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -278,6 +278,16 @@ static llvm::Value *EmitOverflowIntrinsic(CodeGenFunction &CGF, 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); +} + RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -301,19 +311,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_stdarg_start: case Builtin::BI__builtin_va_start: case Builtin::BI__va_start: - case Builtin::BI__builtin_va_end: { - Value *ArgValue = (BuiltinID == Builtin::BI__va_start) - ? EmitScalarExpr(E->getArg(0)) - : EmitVAListRef(E->getArg(0)).getPointer(); - 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_end: + return RValue::get( + EmitVAStartEnd(BuiltinID == Builtin::BI__va_start + ? EmitScalarExpr(E->getArg(0)) + : EmitVAListRef(E->getArg(0)).getPointer(), + BuiltinID != Builtin::BI__builtin_va_end)); case Builtin::BI__builtin_va_copy: { Value *DstPtr = EmitVAListRef(E->getArg(0)).getPointer(); Value *SrcPtr = EmitVAListRef(E->getArg(1)).getPointer(); @@ -5896,6 +5899,31 @@ BuildVector(ArrayRef Ops) { 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)).getPointer(), + BuiltinID == X86::BI__builtin_ms_va_start); + if (BuiltinID == X86::BI__builtin_ms_va_copy) { + // Lower this manually. We can't reliably determine whether or not any + // given va_copy() is for a Win64 va_list from the calling convention + // alone, because it's legal to do this from a System V ABI function. + // With opaque pointer types, we won't have enough information in LLVM + // IR to determine this from the argument types, either. Best to do it + // now, while we have enough information. + Address DestAddr = EmitMSVAListRef(E->getArg(0)); + Address SrcAddr = EmitMSVAListRef(E->getArg(1)); + + llvm::Type *BPP = Int8PtrPtrTy; + + DestAddr = Address(Builder.CreateBitCast(DestAddr.getPointer(), BPP, "cp"), + DestAddr.getAlignment()); + SrcAddr = Address(Builder.CreateBitCast(SrcAddr.getPointer(), BPP, "ap"), + SrcAddr.getAlignment()); + + Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val"); + return Builder.CreateStore(ArgPtr, DestAddr); + } + SmallVector Ops; // Find out if any arguments are required to be integer constant expressions. diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 1027661730..f4cd314550 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -3594,6 +3594,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, /* VarArg handling */ -Address CodeGenFunction::EmitVAArg(Address VAListAddr, QualType Ty) { +Address CodeGenFunction::EmitVAArg(VAArgExpr *VE, Address &VAListAddr) { + VAListAddr = VE->isMicrosoftABI() + ? EmitMSVAListRef(VE->getSubExpr()) + : EmitVAListRef(VE->getSubExpr()); + QualType Ty = VE->getType(); + if (VE->isMicrosoftABI()) + return CGM.getTypes().getABIInfo().EmitMSVAArg(*this, VAListAddr, Ty); return CGM.getTypes().getABIInfo().EmitVAArg(*this, VAListAddr, Ty); } diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 1ab9e022c8..a229d94ad9 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -960,8 +960,8 @@ void AggExprEmitter::VisitChooseExpr(const ChooseExpr *CE) { } void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) { - Address ArgValue = CGF.EmitVAListRef(VE->getSubExpr()); - Address ArgPtr = CGF.EmitVAArg(ArgValue, VE->getType()); + Address ArgValue = Address::invalid(); + Address ArgPtr = CGF.EmitVAArg(VE, ArgValue); if (!ArgPtr.isValid()) { // If EmitVAArg fails, we fall back to the LLVM instruction. diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index a2eba2b4cb..2f9d0cab24 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -1011,8 +1011,8 @@ ComplexPairTy ComplexExprEmitter::VisitInitListExpr(InitListExpr *E) { } ComplexPairTy ComplexExprEmitter::VisitVAArgExpr(VAArgExpr *E) { - Address ArgValue = CGF.EmitVAListRef(E->getSubExpr()); - Address ArgPtr = CGF.EmitVAArg(ArgValue, E->getType()); + Address ArgValue = Address::invalid(); + Address ArgPtr = CGF.EmitVAArg(E, ArgValue); if (!ArgPtr.isValid()) { CGF.ErrorUnsupported(E, "complex va_arg expression"); diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 2bc264cfa1..7bf2cba5d8 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -3357,8 +3357,9 @@ Value *ScalarExprEmitter::VisitVAArgExpr(VAArgExpr *VE) { if (Ty->isVariablyModifiedType()) CGF.EmitVariablyModifiedType(Ty); - Address ArgValue = CGF.EmitVAListRef(VE->getSubExpr()); - Address ArgPtr = CGF.EmitVAArg(ArgValue, VE->getType()); + Address ArgValue = Address::invalid(); + Address ArgPtr = CGF.EmitVAArg(VE, ArgValue); + llvm::Type *ArgTy = ConvertType(VE->getType()); // If EmitVAArg fails, we fall back to the LLVM instruction. diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index c948f8fdd9..8801c3b3f2 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -1718,6 +1718,10 @@ Address CodeGenFunction::EmitVAListRef(const Expr* E) { return EmitLValue(E).getAddress(); } +Address CodeGenFunction::EmitMSVAListRef(const Expr *E) { + return EmitLValue(E).getAddress(); +} + void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E, llvm::Constant *Init) { assert (Init && "Invalid DeclRefExpr initializer!"); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index cf04596772..8879169a0f 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1655,6 +1655,11 @@ public: // or the value of the expression, depending on how va_list is defined. Address EmitVAListRef(const Expr *E); + /// 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. + Address 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); @@ -1752,11 +1757,23 @@ public: /// to -1 in accordance with the Itanium C++ ABI. void EmitNullInitialization(Address DestPtr, 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. + /// 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); + + /// Generate code to get an argument from the passed in pointer + /// and update it accordingly. + /// \param VE The \c VAArgExpr for which to generate code. + /// \param VAListAddr Receives a reference to the \c va_list as emitted by + /// either \c EmitVAListRef or \c EmitMSVAListRef. + /// \returns A pointer to the argument. // 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. - Address EmitVAArg(Address VAListAddr, QualType Ty); + Address EmitVAArg(VAArgExpr *VE, Address &VAListAddr); /// emitArrayLength - Compute the length of an array, even if it's a /// VLA, and drill down to the base element type. diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 201bebf9db..07c706b49a 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -61,6 +61,11 @@ ABIInfo::getNaturalAlignIndirectInReg(QualType Ty, bool Realign) const { /*ByRef*/ false, Realign); } +Address ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const { + return Address::invalid(); +} + ABIInfo::~ABIInfo() {} static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, @@ -1734,6 +1739,8 @@ public: Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty) const override; + Address EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const override; bool has64BitPointers() const { return Has64BitPointers; @@ -3266,6 +3273,14 @@ Address X86_64ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, return ResAddr; } +Address X86_64ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const { + return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, + CGF.getContext().getTypeInfoInChars(Ty), + CharUnits::fromQuantity(8), + /*allowHigherAlign*/ false); +} + ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs, bool IsReturnType) const { diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index b009123c8c..cc509566bd 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -249,6 +249,12 @@ void Sema::Initialize() { } } + 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); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index c6a00b1dad..f8e6c9d5ee 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -1037,6 +1037,8 @@ bool Sema::CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { default: return false; case X86::BI__builtin_cpu_supports: return SemaBuiltinCpuSupports(TheCall); + case X86::BI__builtin_ms_va_start: + return SemaBuiltinMSVAStart(TheCall); case X86::BI_mm_prefetch: i = 1; l = 0; u = 3; break; case X86::BI__builtin_ia32_sha1rnds4: i = 2, l = 0; u = 3; break; case X86::BI__builtin_ia32_vpermil2pd: @@ -2317,9 +2319,10 @@ bool Sema::CheckObjCString(Expr *Arg) { return false; } -/// SemaBuiltinVAStart - Check the arguments to __builtin_va_start for validity. -/// Emit an error and return true on failure, return false on success. -bool Sema::SemaBuiltinVAStart(CallExpr *TheCall) { +/// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start' +/// for validity. Emit an error and return true on failure; return false +/// on success. +bool Sema::SemaBuiltinVAStartImpl(CallExpr *TheCall) { Expr *Fn = TheCall->getCallee(); if (TheCall->getNumArgs() > 2) { Diag(TheCall->getArg(2)->getLocStart(), @@ -2397,6 +2400,48 @@ bool Sema::SemaBuiltinVAStart(CallExpr *TheCall) { return false; } +/// Check the arguments to '__builtin_va_start' for validity, and that +/// it was called from a function of the native ABI. +/// Emit an error and return true on failure; return false on success. +bool Sema::SemaBuiltinVAStart(CallExpr *TheCall) { + // On x86-64 Unix, don't allow this in Win64 ABI functions. + // On x64 Windows, don't allow this in System V ABI functions. + // (Yes, that means there's no corresponding way to support variadic + // System V ABI functions on Windows.) + if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86_64) { + unsigned OS = Context.getTargetInfo().getTriple().getOS(); + clang::CallingConv CC = CC_C; + if (const FunctionDecl *FD = getCurFunctionDecl()) + CC = FD->getType()->getAs()->getCallConv(); + if ((OS == llvm::Triple::Win32 && CC == CC_X86_64SysV) || + (OS != llvm::Triple::Win32 && CC == CC_X86_64Win64)) + return Diag(TheCall->getCallee()->getLocStart(), + diag::err_va_start_used_in_wrong_abi_function) + << (OS != llvm::Triple::Win32); + } + return SemaBuiltinVAStartImpl(TheCall); +} + +/// Check the arguments to '__builtin_ms_va_start' for validity, and that +/// it was called from a Win64 ABI function. +/// Emit an error and return true on failure; return false on success. +bool Sema::SemaBuiltinMSVAStart(CallExpr *TheCall) { + // This only makes sense for x86-64. + const llvm::Triple &TT = Context.getTargetInfo().getTriple(); + Expr *Callee = TheCall->getCallee(); + if (TT.getArch() != llvm::Triple::x86_64) + return Diag(Callee->getLocStart(), diag::err_x86_builtin_32_bit_tgt); + // Don't allow this in System V ABI functions. + clang::CallingConv CC = CC_C; + if (const FunctionDecl *FD = getCurFunctionDecl()) + CC = FD->getType()->getAs()->getCallConv(); + if (CC == CC_X86_64SysV || + (TT.getOS() != llvm::Triple::Win32 && CC != CC_X86_64Win64)) + return Diag(Callee->getLocStart(), + diag::err_ms_va_start_used_in_sysv_function); + return SemaBuiltinVAStartImpl(TheCall); +} + bool Sema::SemaBuiltinVAStartARM(CallExpr *Call) { // void __va_start(va_list *ap, const char *named_addr, size_t slot_size, // const char *named_addr); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6b20f59046..06b96aa0ac 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11671,43 +11671,57 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc) { Expr *OrigExpr = E; + bool IsMS = false; + + // It might be a __builtin_ms_va_list. (But don't ever mark a va_arg() + // as Microsoft ABI on an actual Microsoft platform, where + // __builtin_ms_va_list and __builtin_va_list are the same.) + if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinMSVaList() && + Context.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList) { + 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.get(); - } 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.getAs(); - } 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.get(); + } 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.getAs(); + } 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(), @@ -11749,7 +11763,7 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, } QualType T = TInfo->getType().getNonLValueExprType(Context); - return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T); + return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS); } ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 9c5586bbe1..45837285cd 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6270,6 +6270,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) { case PREDEF_DECL_VA_LIST_TAG: return Context.getVaListTagDecl(); + case PREDEF_DECL_BUILTIN_MS_VA_LIST_ID: + return Context.getBuiltinMSVaListDecl(); + case PREDEF_DECL_EXTERN_C_CONTEXT_ID: return Context.getExternCContextDecl(); } diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 4501dd82b1..5322a40616 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -830,6 +830,7 @@ void ASTStmtReader::VisitVAArgExpr(VAArgExpr *E) { E->setWrittenTypeInfo(GetTypeSourceInfo(Record, Idx)); E->setBuiltinLoc(ReadSourceLocation(Record, Idx)); E->setRParenLoc(ReadSourceLocation(Record, Idx)); + E->setIsMicrosoftABI(Record[Idx++]); } void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 67a7eeaa59..b582c3239d 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4084,6 +4084,8 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, PREDEF_DECL_OBJC_INSTANCETYPE_ID); RegisterPredefDecl(Context.BuiltinVaListDecl, PREDEF_DECL_BUILTIN_VA_LIST_ID); RegisterPredefDecl(Context.VaListTagDecl, PREDEF_DECL_VA_LIST_TAG); + RegisterPredefDecl(Context.BuiltinMSVaListDecl, + PREDEF_DECL_BUILTIN_MS_VA_LIST_ID); RegisterPredefDecl(Context.ExternCContext, PREDEF_DECL_EXTERN_C_CONTEXT_ID); // Build a record containing all of the tentative definitions in this file, in diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index be1c69bf08..7c0424cc87 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -771,6 +771,7 @@ void ASTStmtWriter::VisitVAArgExpr(VAArgExpr *E) { Writer.AddTypeSourceInfo(E->getWrittenTypeInfo(), Record); Writer.AddSourceLocation(E->getBuiltinLoc(), Record); Writer.AddSourceLocation(E->getRParenLoc(), Record); + Record.push_back(E->isMicrosoftABI()); Code = serialization::EXPR_VA_ARG; } diff --git a/test/CodeGen/ms_abi.c b/test/CodeGen/ms_abi.c index 7c5c26fc41..2cca24901b 100644 --- a/test/CodeGen/ms_abi.c +++ b/test/CodeGen/ms_abi.c @@ -1,20 +1,145 @@ // 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() + // FREEBSD: call x86_64_win64cc void @f1() + // WIN64: call void @f1() f2(); -// FREEBSD: call void @f2() -// WIN64: call x86_64_sysvcc void @f2() + // FREEBSD: call void @f2() + // WIN64: call x86_64_sysvcc void @f2() } // FREEBSD: declare x86_64_win64cc void @f1() // FREEBSD: declare void @f2() // 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:.*]] = alloca i8* + // FREEBSD: call void @llvm.va_start + // WIN64: %[[AP:.*]] = alloca i8* + // WIN64: call void @llvm.va_start + int b = __builtin_va_arg(ap, int); + // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32* + // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32* + double _Complex c = __builtin_va_arg(ap, double _Complex); + // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16 + // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }* + // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }* + struct foo d = __builtin_va_arg(ap, struct foo); + // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16 + // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]* + // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]* + __builtin_ms_va_list ap2; + __builtin_ms_va_copy(ap2, ap); + // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] + // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] + __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:.*]] = alloca i8* + // WIN64: call void @llvm.va_start + int b = __builtin_va_arg(ap, int); + // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32* + double _Complex c = __builtin_va_arg(ap, double _Complex); + // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }* + struct foo d = __builtin_va_arg(ap, struct foo); + // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]* + __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 +} + +// Verify that using a Win64 va_list from a System V function works. +void __attribute__((sysv_abi)) f6(__builtin_ms_va_list ap) { + // FREEBSD-LABEL: define void @f6 + // FREEBSD: store i8* %ap, i8** %[[AP:.*]] + // WIN64-LABEL: define x86_64_sysvcc void @f6 + // WIN64: store i8* %ap, i8** %[[AP:.*]] + int b = __builtin_va_arg(ap, int); + // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32* + // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32* + double _Complex c = __builtin_va_arg(ap, double _Complex); + // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16 + // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }* + // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }* + struct foo d = __builtin_va_arg(ap, struct foo); + // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16 + // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]] + // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]* + // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16 + // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]* + __builtin_ms_va_list ap2; + __builtin_ms_va_copy(ap2, ap); + // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] + // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] +} diff --git a/test/PCH/Inputs/va_arg.h b/test/PCH/Inputs/va_arg.h index 1244e9faa3..132759ed02 100644 --- a/test/PCH/Inputs/va_arg.h +++ b/test/PCH/Inputs/va_arg.h @@ -1,2 +1,5 @@ #include +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) diff --git a/test/PCH/va_arg.c b/test/PCH/va_arg.c index dba9eea8de..2bbf3c5ebd 100644 --- a/test/PCH/va_arg.c +++ b/test/PCH/va_arg.c @@ -11,3 +11,9 @@ char *g0(char** argv, int argc) { return argv[argc]; } 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); +} diff --git a/test/PCH/va_arg.cpp b/test/PCH/va_arg.cpp index 0b3c3b107c..627ce23ad5 100644 --- a/test/PCH/va_arg.cpp +++ b/test/PCH/va_arg.cpp @@ -10,8 +10,13 @@ typedef __SIZE_TYPE__ size_t; 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); +} diff --git a/test/PCH/va_arg.h b/test/PCH/va_arg.h index 4a8e5102bc..255c6589a7 100644 --- a/test/PCH/va_arg.h +++ b/test/PCH/va_arg.h @@ -6,3 +6,10 @@ char *f (char * (*g) (char **, int), char **p, ...) { 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)); +} diff --git a/test/Sema/varargs-win64.c b/test/Sema/varargs-win64.c new file mode 100644 index 0000000000..06d1c7f246 --- /dev/null +++ b/test/Sema/varargs-win64.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-pc-win32 + +void __attribute__((sysv_abi)) foo(int a, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, a); // expected-error {{'va_start' used in System V ABI function}} +} diff --git a/test/Sema/varargs-x86-32.c b/test/Sema/varargs-x86-32.c new file mode 100644 index 0000000000..6f57022ffc --- /dev/null +++ b/test/Sema/varargs-x86-32.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i386-apple-darwin9 + +void foo(int a, ...) { + __builtin_ms_va_start((void *)0, a); // expected-error {{this builtin is only available on x86-64 targets}} +} diff --git a/test/Sema/varargs-x86-64.c b/test/Sema/varargs-x86-64.c index 2fe9b10cf7..d50dd6a6fc 100644 --- a/test/Sema/varargs-x86-64.c +++ b/test/Sema/varargs-x86-64.c @@ -6,3 +6,75 @@ void f1() { (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 f2(int a, ...) { + __builtin_ms_va_list ap; + __builtin_ms_va_start(ap, a); // expected-error {{'__builtin_ms_va_start' used in System V ABI function}} +} + +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); // no-warning + (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'}} +} + +void __attribute__((ms_abi)) g10(int a, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, a); // expected-error {{'va_start' used in Win64 ABI function}} +} diff --git a/test/SemaTemplate/instantiate-expr-3.cpp b/test/SemaTemplate/instantiate-expr-3.cpp index ca88b00300..90c322cbf3 100644 --- a/test/SemaTemplate/instantiate-expr-3.cpp +++ b/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 @@ template 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; // expected-note{{instantiation}} + +template +struct VaArg2 { + void __attribute__((ms_abi)) 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; + +template +struct VaArg3 { + void __attribute__((ms_abi)) 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; // expected-note{{instantiation}}