diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -4366,6 +4366,16 @@ if (isAggregateTypeForABI(Ty) || Ty->isMemberPointerType()) { uint64_t Width = getContext().getTypeSize(Ty); IsIndirect = Width > 64 || !llvm::isPowerOf2_64(Width); + } else if (IsMingw64) { + if (const BuiltinType *BT = Ty->getAs()) { + if (BT->getKind() == BuiltinType::LongDouble) { + // Mingw64 GCC uses the old 80 bit extended precision floating point + // unit. It passes them indirectly through memory. + const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat(); + if (LDF == &llvm::APFloat::x87DoubleExtended()) + IsIndirect = true; + } + } } return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect, diff --git a/clang/test/CodeGen/mingw-long-double.c b/clang/test/CodeGen/mingw-long-double.c --- a/clang/test/CodeGen/mingw-long-double.c +++ b/clang/test/CodeGen/mingw-long-double.c @@ -45,3 +45,16 @@ // GNU32: declare dso_local void @__mulxc3 // GNU64: declare dso_local void @__mulxc3 // MSC64: declare dso_local void @__muldc3 + +void VarArgLD(int a, ...) { + // GNU32-LABEL: define{{.*}} void @VarArgLD + // GNU64-LABEL: define{{.*}} void @VarArgLD + // MSC64-LABEL: define{{.*}} void @VarArgLD + __builtin_va_list ap; + __builtin_va_start(ap, a); + long double LD = __builtin_va_arg(ap, long double); + // GNU32: bitcast i8* %argp.cur to x86_fp80* + // GNU64: bitcast i8* %argp.cur to x86_fp80** + // MSC64: bitcast i8* %argp.cur to double* + __builtin_va_end(ap); +}