[clang] [AArch64] Fix handling of HFAs passed to Windows variadic functions

The documentation says that for variadic functions, all composites
are treated similarly, no special handling of HFAs/HVAs, not even
for the fixed arguments of a variadic function.

Differential Revision: https://reviews.llvm.org/D100467

GitOrigin-RevId: 8e0f2e89ff951c74875ed751e2215cc263b33328
diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp
index 3ff3eed..bd3c265 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
@@ -5418,7 +5418,8 @@
   bool isDarwinPCS() const { return Kind == DarwinPCS; }
 
   ABIArgInfo classifyReturnType(QualType RetTy, bool IsVariadic) const;
-  ABIArgInfo classifyArgumentType(QualType RetTy) const;
+  ABIArgInfo classifyArgumentType(QualType RetTy, bool IsVariadic,
+                                  unsigned CallingConvention) const;
   ABIArgInfo coerceIllegalVector(QualType Ty) const;
   bool isHomogeneousAggregateBaseType(QualType Ty) const override;
   bool isHomogeneousAggregateSmallEnough(const Type *Ty,
@@ -5432,7 +5433,8 @@
           classifyReturnType(FI.getReturnType(), FI.isVariadic());
 
     for (auto &it : FI.arguments())
-      it.info = classifyArgumentType(it.type);
+      it.info = classifyArgumentType(it.type, FI.isVariadic(),
+                                     FI.getCallingConvention());
   }
 
   Address EmitDarwinVAArg(Address VAListAddr, QualType Ty,
@@ -5635,7 +5637,9 @@
   return getNaturalAlignIndirect(Ty, /*ByVal=*/false);
 }
 
-ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty) const {
+ABIArgInfo
+AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadic,
+                                     unsigned CallingConvention) const {
   Ty = useFirstFieldIfTransparentUnion(Ty);
 
   // Handle illegal vector types here.
@@ -5681,7 +5685,11 @@
   // Homogeneous Floating-point Aggregates (HFAs) need to be expanded.
   const Type *Base = nullptr;
   uint64_t Members = 0;
-  if (isHomogeneousAggregate(Ty, Base, Members)) {
+  bool IsWin64 = Kind == Win64 || CallingConvention == llvm::CallingConv::Win64;
+  bool IsWinVariadic = IsWin64 && IsVariadic;
+  // In variadic functions on Windows, all composite types are treated alike,
+  // no special handling of HFAs/HVAs.
+  if (!IsWinVariadic && isHomogeneousAggregate(Ty, Base, Members)) {
     return ABIArgInfo::getDirect(
         llvm::ArrayType::get(CGT.ConvertType(QualType(Base, 0)), Members));
   }
@@ -5838,10 +5846,10 @@
   return Members <= 4;
 }
 
-Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr,
-                                            QualType Ty,
-                                            CodeGenFunction &CGF) const {
-  ABIArgInfo AI = classifyArgumentType(Ty);
+Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
+                                       CodeGenFunction &CGF) const {
+  ABIArgInfo AI = classifyArgumentType(Ty, /*IsVariadic=*/true,
+                                       CGF.CurFnInfo->getCallingConvention());
   bool IsIndirect = AI.isIndirect();
 
   llvm::Type *BaseTy = CGF.ConvertType(Ty);
diff --git a/test/CodeGen/ms_abi_aarch64.c b/test/CodeGen/ms_abi_aarch64.c
index 8526ce9..cf24442 100644
--- a/test/CodeGen/ms_abi_aarch64.c
+++ b/test/CodeGen/ms_abi_aarch64.c
@@ -84,3 +84,39 @@
   __builtin_va_end(ap);
   // WIN64: call void @llvm.va_end
 }
+
+struct HFA {
+  float a, b, c;
+};
+
+__attribute__((ms_abi)) void msabi_hfa(struct HFA a);
+__attribute__((ms_abi)) void msabi_hfa_vararg(struct HFA a, int b, ...);
+
+void call_msabi_hfa(void) {
+  // COMMON-LABEL: define{{.*}} void @call_msabi_hfa()
+  // WIN64: call void @msabi_hfa([3 x float] {{.*}})
+  // LINUX: call win64cc void @msabi_hfa([3 x float] {{.*}})
+  msabi_hfa((struct HFA){1.0f, 2.0f, 3.0f});
+}
+
+void call_msabi_hfa_vararg(void) {
+  // COMMON-LABEL: define{{.*}} void @call_msabi_hfa_vararg()
+  // WIN64: call void ([2 x i64], i32, ...) @msabi_hfa_vararg([2 x i64] {{.*}}, i32 4, [2 x i64] {{.*}})
+  // LINUX: call win64cc void ([2 x i64], i32, ...) @msabi_hfa_vararg([2 x i64] {{.*}}, i32 4, [2 x i64] {{.*}})
+  msabi_hfa_vararg((struct HFA){1.0f, 2.0f, 3.0f}, 4,
+                   (struct HFA){5.0f, 6.0f, 7.0f});
+}
+
+__attribute__((ms_abi)) void get_msabi_hfa_vararg(int a, ...) {
+  // COMMON-LABEL: define{{.*}} void @get_msabi_hfa_vararg
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  // COMMON: %[[AP:.*]] = alloca i8*
+  // COMMON: call void @llvm.va_start
+  struct HFA b = __builtin_va_arg(ap, struct HFA);
+  // COMMON: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // COMMON-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 16
+  // COMMON-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // COMMON-NEXT: bitcast i8* %[[AP_CUR]] to %struct.HFA*
+  __builtin_ms_va_end(ap);
+}