[clang] [clang] [x86] Rewrite the Sys V Classification Algorithm to Better Match SysV ABI and gcc (PR #221746)
Benjamin Luke via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 13 13:18:08 PDT 2026
https://github.com/freaknbigpanda updated https://github.com/llvm/llvm-project/pull/221746
>From e869ccf42d1ac4f205a72bbd6704fcb58bb94480 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Fri, 20 Mar 2026 15:21:45 -0700
Subject: [PATCH 01/12] [clang][X86] Refactor X86_64AbiInfo::classify to
properly implement psABI sepc for types >128bits
todo: fill out commit message later
---
clang/include/clang/Basic/ABIVersions.def | 5 +
clang/lib/CodeGen/Targets/X86.cpp | 821 +++++++++++++++---
clang/test/CodeGen/X86/x86-illegal-vector.c | 22 +
.../CodeGen/X86/x86-small-fp-vector-abi.c | 58 ++
clang/test/CodeGen/X86/x86-vec-i128.c | 28 +-
.../CodeGen/X86/x86_64-atomic-vector-abi.c | 92 ++
clang/test/CodeGen/builtin-masked.c | 92 +-
.../CodeGen/builtin-maximumnum-minimumnum.c | 18 +-
clang/test/CodeGen/builtin-maxnum-minnum.c | 18 +-
clang/test/CodeGen/nofpclass.c | 514 +++++------
clang/test/CodeGen/sysv_abi.c | 28 +-
clang/test/CodeGenCXX/ext-int-vector-abi.cpp | 8 +-
.../test/CodeGenCXX/matrix-vector-bit-int.cpp | 20 +-
.../x86_64-empty-base-vector-abi.cpp | 51 ++
14 files changed, 1268 insertions(+), 507 deletions(-)
create mode 100644 clang/test/CodeGen/X86/x86-illegal-vector.c
create mode 100644 clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
create mode 100644 clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
create mode 100644 clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index 3c434da91bfab..a1da7a8ea3a0a 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -147,6 +147,11 @@ ABI_VER_MAJOR(22)
/// Attempt to be ABI-compatible with code generated by Clang 23.0.x.
/// This causes clang to:
+/// - Use the pre-Clang 24 x86_64 SysV argument and return classifier.
+/// This preserves older, but incorrect according to the SysV spec, vector
+/// and aggregate signatures using the historical two-eightbyte model
+/// instead of classifying every eightbyte in larger vector and aggregate
+/// types.
/// - Ignore per-function target attributes when determining the x86 AVX ABI
/// level.
/// - On SPARC, pass a `_Complex` value with an integer element type
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 14fe5ffae8372..a4334f40374fe 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -8,9 +8,13 @@
#include "ABIInfoImpl.h"
#include "TargetInfo.h"
+#include "clang/AST/TypeBase.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cstdint>
using namespace clang;
using namespace clang::CodeGen;
@@ -1238,22 +1242,6 @@ class X86_64ABIInfo : public ABIInfo {
/// should just return Memory for the aggregate).
static Class merge(Class Accum, Class Field);
- /// postMerge - Implement the X86_64 ABI post merging algorithm.
- ///
- /// Post merger cleanup, reduces a malformed Hi and Lo pair to
- /// final MEMORY or SSE classes when necessary.
- ///
- /// \param AggregateSize - The size of the current aggregate in
- /// the classification process.
- ///
- /// \param Lo - The classification for the parts of the type
- /// residing in the low word of the containing object.
- ///
- /// \param Hi - The classification for the parts of the type
- /// residing in the higher words of the containing object.
- ///
- void postMerge(unsigned AggregateSize, Class &Lo, Class &Hi) const;
-
/// classify - Determine the x86_64 register classes in which the
/// given type T should be passed.
///
@@ -1280,8 +1268,17 @@ class X86_64ABIInfo : public ABIInfo {
///
/// If the \arg Lo class is ComplexX87, then the \arg Hi class will
/// also be ComplexX87.
- void classify(QualType T, uint64_t OffsetBase, Class &Lo, Class &Hi,
- bool isNamedArg, bool IsRegCall = false) const;
+ void classify(QualType T, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes, bool isNamedArg,
+ bool isRegCall = false) const;
+
+ void classifyClang23(QualType T, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes, bool isNamedArg,
+ bool isRegCall) const;
+
+ void classifyClang24(QualType T, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes,
+ bool isNamedArg) const;
llvm::Type *GetByteVectorType(QualType Ty) const;
llvm::Type *GetSSETypeAtOffset(llvm::Type *IRType,
@@ -1364,6 +1361,16 @@ class X86_64ABIInfo : public ABIInfo {
return true;
}
+ // For Clang 24 the classification algorithm was refactored to properly
+ // implement psABI spec, locking these platforms to the old classification
+ // algorithm to preserve ABI compatibility
+ bool useLegacyClassificationAlgorithm() const {
+ const llvm::Triple &Triple = getTarget().getTriple();
+ return getContext().getLangOpts().getClangABICompat() <=
+ LangOptions::ClangABI::Ver23 ||
+ Triple.isOSDarwin() || Triple.isPS() || Triple.isOSFreeBSD();
+ }
+
X86AVXABILevel AVXLevel;
// Some ABIs (e.g. X32 ABI and Native Client OS) use 32 bit pointers on
// 64-bit hardware.
@@ -1800,39 +1807,6 @@ void WinX86_64TargetCodeGenInfo::setTargetAttributes(
addStackProbeTargetAttributes(D, GV, CGM);
}
-void X86_64ABIInfo::postMerge(unsigned AggregateSize, Class &Lo,
- Class &Hi) const {
- // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done:
- //
- // (a) If one of the classes is Memory, the whole argument is passed in
- // memory.
- //
- // (b) If X87UP is not preceded by X87, the whole argument is passed in
- // memory.
- //
- // (c) If the size of the aggregate exceeds two eightbytes and the first
- // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole
- // argument is passed in memory. NOTE: This is necessary to keep the
- // ABI working for processors that don't support the __m256 type.
- //
- // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE.
- //
- // Some of these are enforced by the merging logic. Others can arise
- // only with unions; for example:
- // union { _Complex double; unsigned; }
- //
- // Note that clauses (b) and (c) were added in 0.98.
- //
- if (Hi == Memory)
- Lo = Memory;
- if (Hi == X87Up && Lo != X87 && honorsRevision0_98())
- Lo = Memory;
- if (AggregateSize > 128 && (Lo != SSE || Hi != SSEUp))
- Lo = Memory;
- if (Hi == SSEUp && Lo != SSE)
- Hi = SSE;
-}
-
static X86AVXABILevel getEffectiveX86AVXABILevel(CodeGenTypes &CGT,
X86AVXABILevel GlobalAVXLevel,
const FunctionDecl *FD) {
@@ -1899,17 +1873,526 @@ X86_64ABIInfo::Class X86_64ABIInfo::merge(Class Accum, Class Field) {
return SSE;
}
-void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
- Class &Hi, bool isNamedArg, bool IsRegCall) const {
- // FIXME: This code can be simplified by introducing a simple value class for
- // Class pairs with appropriate constructor methods for the various
+void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes,
+ bool isNamedArg, bool IsRegCall) const {
+ if (useLegacyClassificationAlgorithm() || IsRegCall) {
+ // RegCall is a separate ABI from the SysV psABI. Keep its classification
+ // on the pre-Clang-24 rules so this SysV classifier refactor
+ // does not silently break ABI.
+ classifyClang23(Ty, OffsetBase, EightBytes, isNamedArg, IsRegCall);
+ } else {
+ classifyClang24(Ty, OffsetBase, EightBytes, isNamedArg);
+ }
+}
+
+void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes,
+ bool isNamedArg) const {
+ // Helpers
+ auto ClassifyAsMemory = [&]() {
+ if (EightBytes.empty())
+ EightBytes.push_back(Memory);
+ else
+ llvm::fill(EightBytes, Memory);
+ };
+
+ auto PostMerge = [&]() {
+ if (llvm::is_contained(EightBytes, Memory)) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ for (unsigned I = 1, E = EightBytes.size(); I != E; ++I) {
+ if (EightBytes[I] == X87Up && EightBytes[I - 1] != X87) {
+ assert(EightBytes.size() == 2 && "Expected two eightbytes");
+ ClassifyAsMemory();
+ return;
+ }
+ }
+
+ if (EightBytes.size() > 2) {
+ if (EightBytes.front() != SSE) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ for (Class C : drop_begin(EightBytes)) {
+ if (C != SSEUp) {
+ ClassifyAsMemory();
+ return;
+ }
+ }
+ }
+
+ for (unsigned I = 1, E = EightBytes.size(); I != E; ++I) {
+ if (EightBytes[I] == SSEUp && EightBytes[I - 1] != SSE &&
+ EightBytes[I - 1] != SSEUp) {
+ EightBytes[I] = SSE;
+ }
+ }
+ };
+
+ auto MergeIntoEightByte = [&](uint64_t Offset, Class C) {
+ if (C == NoClass)
+ return;
+ assert(Offset / 64 < EightBytes.size() &&
+ "classification offset is outside the containing type");
+ EightBytes[Offset / 64] = merge(EightBytes[Offset / 64], C);
+ };
+
+ auto MergeIntoAdjacentEightBytes =
+ [&](uint64_t Offset, SmallVector<Class> ToBeMergedEightBytes) {
+ uint64_t currentOffset = Offset;
+ for (Class ToBeMergedEightByte : ToBeMergedEightBytes) {
+ MergeIntoEightByte(currentOffset, ToBeMergedEightByte);
+ currentOffset = llvm::alignDown(currentOffset, uint64_t(64)) + 64;
+ }
+ };
+
+ auto SetEightByte = [&](uint64_t Offset, Class C) {
+ EightBytes[Offset / 64] = C;
+ };
+
+ auto SetAdjacentEightBytes = [&](uint64_t Offset, Class LoClass,
+ Class HiClass) {
+ SetEightByte(Offset, LoClass);
+ SetEightByte(llvm::alignDown(Offset, uint64_t(64)) + 64, HiClass);
+ };
+
+ auto SetTouchedEightBytes = [&](uint64_t Size, Class C) {
+ for (uint64_t Offset = OffsetBase, End = OffsetBase + Size; Offset < End;
+ Offset = llvm::alignDown(Offset, uint64_t(64)) + 64)
+ SetEightByte(Offset, C);
+ };
+
+ // End Helpers
+
+ // Resize the EigthBytes vector if needed to fit the type
+ uint64_t Size = getContext().getTypeSize(Ty);
+ unsigned Required = (OffsetBase + Size + 63) / 64;
+ if (EightBytes.size() < Required)
+ EightBytes.resize(Required, NoClass);
+
+ if (Size == 0) {
+ EightBytes.resize(1, NoClass);
+ return; // No need to classify empty types, return NoClass
+ }
+
+ // Type classification begin
+
+ // Overflow behaviour types
+ if (const OverflowBehaviorType *OBT = Ty->getAs<OverflowBehaviorType>()) {
+ classifyClang24(OBT->getUnderlyingType(), OffsetBase, EightBytes,
+ isNamedArg);
+ return;
+ }
+
+ // Atomic types usually classify the same way as their value type. If Clang
+ // promotes the atomic storage size, fall back to memory so the full storage
+ // representation is preserved across the ABI boundary.
+ if (const AtomicType *AT = Ty->getAs<AtomicType>()) {
+ QualType ValueTy = AT->getValueType();
+ if (Size != getContext().getTypeSize(ValueTy)) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ classifyClang24(ValueTy, OffsetBase, EightBytes, isNamedArg);
+ return;
+ }
+
+ // Built In Types
+ if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
+ BuiltinType::Kind K = BT->getKind();
+
+ if (K == BuiltinType::Void)
+ return;
+ if (K == BuiltinType::Int128 || K == BuiltinType::UInt128) {
+ SetAdjacentEightBytes(OffsetBase, Integer, Integer);
+ return;
+ }
+ if (K >= BuiltinType::Bool && K <= BuiltinType::LongLong) {
+ SetEightByte(OffsetBase, Integer);
+ return;
+ }
+ if (K == BuiltinType::Float || K == BuiltinType::Double ||
+ K == BuiltinType::Float16 || K == BuiltinType::BFloat16) {
+ SetEightByte(OffsetBase, SSE);
+ return;
+ }
+ if (K == BuiltinType::Float128) {
+ SetAdjacentEightBytes(OffsetBase, SSE, SSEUp);
+ return;
+ }
+ if (K == BuiltinType::LongDouble) {
+ const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
+ if (LDF == &llvm::APFloat::IEEEquad())
+ SetAdjacentEightBytes(OffsetBase, SSE, SSEUp);
+ else if (LDF == &llvm::APFloat::x87DoubleExtended())
+ SetAdjacentEightBytes(OffsetBase, X87, X87Up);
+ else if (LDF == &llvm::APFloat::IEEEdouble())
+ SetEightByte(OffsetBase, SSE);
+ else
+ llvm_unreachable("unexpected long double representation!");
+ return;
+ }
+ ClassifyAsMemory();
+ return;
+ }
+
+ // Enums
+ if (const auto *ED = Ty->getAsEnumDecl()) {
+ // Classify the underlying integer type.
+ classifyClang24(ED->getIntegerType(), OffsetBase, EightBytes, isNamedArg);
+ return;
+ }
+
+ // Ptrs
+ if (Ty->hasPointerRepresentation() || Ty->isPipeType()) {
+ SetEightByte(OffsetBase, Integer);
+ return;
+ }
+
+ // C++ pointer-to-member types have integer-class storage: data member
+ // pointers are one ptrdiff_t, and member function pointers are two.
+ if (Ty->isMemberPointerType()) {
+ SetTouchedEightBytes(Size, Integer);
+ return;
+ }
+
+ // Vector & Matrix types
+ // Matrix types are "flattened" and then classified as a m*n vector with the
+ // same element type.
+ const VectorType *VT = Ty->getAs<VectorType>();
+ const MatrixType *MT = Ty->getAs<MatrixType>();
+ bool IsVectorOrMatrix = VT != nullptr || MT != nullptr;
+ if (IsVectorOrMatrix) {
+ QualType ElementType;
+ bool IsSingleElementVector = false;
+ if (VT) {
+ ElementType = VT->getElementType();
+ IsSingleElementVector = VT->getNumElements() == 1;
+ } else if (MT) {
+ ElementType = MT->getElementType();
+ } else {
+ llvm_unreachable("invalid vector or matrix type");
+ }
+ ElementType = getContext().getCanonicalType(ElementType);
+
+ auto IsFPVectorElement = [&]() {
+ return ElementType->isFloat16Type() || ElementType->isBFloat16Type() ||
+ ElementType == getContext().FloatTy ||
+ ElementType == getContext().DoubleTy;
+ };
+
+ auto SetEightByteClasses = [&](Class FirstClass, Class RestClass) {
+ // Vector objects should not start partway through one eightbyte and
+ // cross into another here. Aggregate classification rejects unaligned
+ // non-bit-field vector fields as MEMORY before recursing. Matrix types
+ // use element alignment, so they can still have a partial first chunk.
+ [[maybe_unused]] bool HasPartialFirstEightByte =
+ OffsetBase % 64 && OffsetBase / 64 != (OffsetBase + Size - 1) / 64;
+ if (VT)
+ assert(!HasPartialFirstEightByte &&
+ "unaligned vector should have been classified as memory");
+
+ SetEightByte(OffsetBase, FirstClass);
+ for (uint64_t Offset = llvm::alignDown(OffsetBase, uint64_t(64)) + 64,
+ End = OffsetBase + Size;
+ Offset < End; Offset += 64)
+ SetEightByte(Offset, RestClass);
+ };
+
+ if (IsSingleElementVector && IsFPVectorElement()) {
+ // GCC passes single-element floating-point vectors in memory. This is a
+ // compatibility rule, not a psABI classification rule.
+ ClassifyAsMemory();
+ return;
+ }
+
+ // Vectors of __int128 wider than 128 bits classify as MEMORY: each
+ // __int128 lane contributes two INTEGER eightbytes, and post-merge cleanup
+ // only keeps wider objects in registers when they are SSE followed by
+ // SSEUp.
+ if (passInt128VectorsInMem() && Size > 128 &&
+ (ElementType->isSpecificBuiltinType(BuiltinType::Int128) ||
+ ElementType->isSpecificBuiltinType(BuiltinType::UInt128))) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ // The psABI explicitly classifies named vector types like __m64 and __m128
+ // as SSE/SSEUP, but it does not spell out arbitrary GNU vector extension
+ // integer vectors. We decided to match GCC 15.2.0 which classifies
+ // integer vectors up to 32 bits as INTEGER, while 64-bit and wider integer
+ // vectors use SSE/SSEUP vector classes.
+ if (Size <= 32) {
+ // The ABI classifies floating-point vector elements as SSE. Small
+ // integer vectors are passed as INTEGER, matching GCC for:
+ // 4 bytes - <4 x char>, <2 x short>, <1 x int>
+ // 2 bytes - <2 x char>, <1 x short>
+ // 1 byte - <1 x char>
+ Class C = IsFPVectorElement() ? SSE : Integer;
+
+ SetEightByteClasses(C, C);
+ } else if (Size == 64) {
+ SetEightByteClasses(SSE, SSE);
+ } else if (Size == 128 ||
+ (isNamedArg && Size <= getNativeVectorSizeForAVXABI(AVXLevel))) {
+ SetEightByteClasses(SSE, SSEUp);
+ } else {
+ // Any other types that are not explicity handled should be passed as
+ // memory
+ ClassifyAsMemory();
+ }
+
+ return;
+ }
+
+ // Complex types
+ if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
+ QualType ET = getContext().getCanonicalType(CT->getElementType());
+
+ uint64_t Size = getContext().getTypeSize(Ty);
+ if (ET->isIntegralOrEnumerationType()) {
+ if (Size <= 64)
+ SetEightByte(OffsetBase, Integer);
+ else if (Size <= 128)
+ SetAdjacentEightBytes(OffsetBase, Integer, Integer);
+ } else if (ET->isFloat16Type() || ET == getContext().FloatTy ||
+ ET->isBFloat16Type()) {
+ SetEightByte(OffsetBase, SSE);
+ } else if (ET == getContext().DoubleTy) {
+ SetAdjacentEightBytes(OffsetBase, SSE, SSE);
+ } else if (ET == getContext().LongDoubleTy) {
+ const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
+ if (LDF == &llvm::APFloat::IEEEquad())
+ ClassifyAsMemory();
+ else if (LDF == &llvm::APFloat::x87DoubleExtended())
+ SetEightByte(OffsetBase, ComplexX87);
+ else if (LDF == &llvm::APFloat::IEEEdouble())
+ SetAdjacentEightBytes(OffsetBase, SSE, SSE);
+ else
+ llvm_unreachable("unexpected long double representation!");
+ } else if (ET->isFloat128Type()) {
+ ClassifyAsMemory();
+ }
+
+ // If this complex type crosses an eightbyte boundary then it
+ // should be split.
+ uint64_t EB_Real = (OffsetBase) / 64;
+ uint64_t EB_Imag = (OffsetBase + getContext().getTypeSize(ET)) / 64;
+ if (EightBytes[EB_Imag] == NoClass && EB_Real != EB_Imag)
+ SetAdjacentEightBytes(OffsetBase, EightBytes[EB_Real],
+ EightBytes[EB_Real]);
+
+ return;
+ }
+
+ // BitInt Types
+ if (const auto *EITy = Ty->getAs<BitIntType>()) {
+ if (EITy->getNumBits() <= 64)
+ SetEightByte(OffsetBase, Integer);
+ else if (EITy->getNumBits() <= 128)
+ SetAdjacentEightBytes(OffsetBase, Integer, Integer);
+ else {
+ // Larger values need to get passed in memory.
+ ClassifyAsMemory();
+ }
+
+ return;
+ }
+
+ // Arrays
+ if (const ConstantArrayType *AT = getContext().getAsConstantArrayType(Ty)) {
+ // Arrays are treated like structures.
+
+ // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
+ // than eight eightbytes, ..., it has class MEMORY.
+ if (Size > 512) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ // AMD64-ABI 3.2.3p2: Rule 1. If ..., or it contains unaligned
+ // fields, it has class MEMORY.
+ //
+ // Only need to check alignment of array base.
+ if (OffsetBase % getContext().getTypeAlign(AT->getElementType())) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ // Otherwise implement simplified merge. We could be smarter about
+ // this, but it isn't worth it and would be harder to verify.
+ uint64_t EltSize = getContext().getTypeSize(AT->getElementType());
+ uint64_t ArraySize = AT->getZExtSize();
+
+ for (uint64_t i = 0, Offset = OffsetBase; i < ArraySize;
+ ++i, Offset += EltSize) {
+ SmallVector<Class> ArrayElEightBytes;
+ classifyClang24(AT->getElementType(), Offset % 64, ArrayElEightBytes,
+ isNamedArg);
+ MergeIntoAdjacentEightBytes(Offset, ArrayElEightBytes);
+ if (llvm::is_contained(EightBytes, Memory))
+ break;
+ }
+
+ PostMerge();
+ return;
+ }
+
+ // CXX Record Type
+ if (const RecordType *RT = Ty->getAsCanonical<RecordType>()) {
+ // uint64_t Size = getContext().getTypeSize(Ty);
+
+ // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
+ // than eight eightbytes, ..., it has class MEMORY.
+ if (Size > 512) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial
+ // copy constructor or a non-trivial destructor, it is passed by
+ // invisible reference.
+ if (getRecordArgABI(RT, getCXXABI())) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
+
+ // Assume variable sized types are passed in memory.
+ if (RD->hasFlexibleArrayMember()) {
+ ClassifyAsMemory();
+ return;
+ }
+
+ const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
+
+ // If this is a C++ record, classify the bases first.
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const auto &I : CXXRD->bases()) {
+ assert(!I.isVirtual() && !I.getType()->isDependentType() &&
+ "Unexpected base class!");
+ const auto *Base = I.getType()->castAsCXXRecordDecl();
+
+ // Classify this field.
+ //
+ // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate exceeds
+ // a single eightbyte, each is classified separately. Each
+ // eightbyte gets initialized to class NO_CLASS.
+ SmallVector<Class> BaseEightBytes;
+ uint64_t Offset =
+ OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base));
+ classifyClang24(I.getType(), Offset % 64, BaseEightBytes, isNamedArg);
+ // If the base is memory the whole record will be passed as memory
+ if (BaseEightBytes[0] == Memory) {
+ ClassifyAsMemory();
+ return;
+ }
+ MergeIntoAdjacentEightBytes(Offset, BaseEightBytes);
+ }
+ }
+
+ // Classify the fields one at a time, merging the results.
+ unsigned idx = 0;
+
+ for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
+ i != e; ++i, ++idx) {
+ uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx);
+ bool BitField = i->isBitField();
+
+ // Ignore padding bit-fields.
+ if (BitField && i->isUnnamedBitField())
+ continue;
+
+ bool IsInMemory =
+ Offset % getContext().getTypeAlign(i->getType().getCanonicalType());
+ // Note, skip this test for bit-fields, see below.
+ if (!BitField && IsInMemory) {
+ ClassifyAsMemory();
+ PostMerge();
+ return;
+ }
+
+ // Classify this field.
+ //
+ // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate
+ // exceeds a single eightbyte, each is classified
+ // separately. Each eightbyte gets initialized to class
+ // NO_CLASS.
+ // Bit-fields require special handling, they do not force the
+ // structure to be passed in memory even if unaligned, and
+ // therefore they can straddle an eightbyte.
+ if (BitField) {
+ assert(!i->isUnnamedBitField());
+ uint64_t BitSize = i->getBitWidthValue();
+ for (uint64_t BitOffset = Offset, End = Offset + BitSize;
+ BitOffset < End;
+ BitOffset = llvm::alignDown(BitOffset, uint64_t(64)) + 64)
+ MergeIntoEightByte(BitOffset, Integer);
+ } else {
+ SmallVector<Class> FieldEightBytes;
+ classifyClang24(i->getType(), Offset % 64, FieldEightBytes, isNamedArg);
+ MergeIntoAdjacentEightBytes(Offset, FieldEightBytes);
+ }
+
+ if (llvm::is_contained(EightBytes, Memory))
+ break;
+ }
+
+ PostMerge();
+ }
+}
+
+void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
+ SmallVectorImpl<Class> &EightBytes,
+ bool isNamedArg, bool IsRegCall) const {
+ // FIXME: This code can be simplified by introducing a simple value class
+ // for Class pairs with appropriate constructor methods for the various
// situations.
// FIXME: Some of the split computations are wrong; unaligned vectors
// shouldn't be passed in registers for example, so there is no chance they
// can straddle an eightbyte. Verify & simplify.
+ if (EightBytes.empty())
+ EightBytes.resize(2, NoClass);
- Lo = Hi = NoClass;
+ Class &Lo = EightBytes[0];
+ Class &Hi = EightBytes[1];
+ auto PostMerge = [&](unsigned AggregateSize) {
+ // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done:
+ //
+ // (a) If one of the classes is Memory, the whole argument is passed in
+ // memory.
+ //
+ // (b) If X87UP is not preceded by X87, the whole argument is passed in
+ // memory.
+ //
+ // (c) If the size of the aggregate exceeds two eightbytes and the first
+ // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole
+ // argument is passed in memory. NOTE: This is necessary to keep the
+ // ABI working for processors that don't support the __m256 type.
+ //
+ // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE.
+ //
+ // Some of these are enforced by the merging logic. Others can arise only
+ // with unions; for example:
+ // union { _Complex double; unsigned; }
+ //
+ // Note that clauses (b) and (c) were added in 0.98.
+ if (Hi == Memory)
+ Lo = Memory;
+ if (Hi == X87Up && Lo != X87 && honorsRevision0_98())
+ Lo = Memory;
+ if (AggregateSize > 128 && (Lo != SSE || Hi != SSEUp))
+ Lo = Memory;
+ if (Hi == SSEUp && Lo != SSE)
+ Hi = SSE;
+ };
Class &Current = OffsetBase < 64 ? Lo : Hi;
Current = Memory;
@@ -1950,7 +2433,9 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
if (const auto *ED = Ty->getAsEnumDecl()) {
// Classify the underlying integer type.
- classify(ED->getIntegerType(), OffsetBase, Lo, Hi, isNamedArg);
+ SmallVector<Class> IntEightBytes = {Lo, Hi};
+ classifyClang23(ED->getIntegerType(), OffsetBase, IntEightBytes, isNamedArg,
+ IsRegCall);
return;
}
@@ -2031,9 +2516,9 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
return;
// Arguments of 256-bits are split into four eightbyte chunks. The
- // least significant one belongs to class SSE and all the others to class
- // SSEUP. The original Lo and Hi design considers that types can't be
- // greater than 128-bits, so a 64-bit split in Hi and Lo makes sense.
+ // least significant one belongs to class SSE and all the others to
+ // class SSEUP. The original Lo and Hi design considers that types can't
+ // be greater than 128-bits, so a 64-bit split in Hi and Lo makes sense.
// This design isn't correct for 256-bits, but since there're no cases
// where the upper parts would need to be inspected, avoid adding
// complexity and just consider Hi to match the 64-256 part.
@@ -2042,8 +2527,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
// registers if they are "named", i.e. not part of the "..." of a
// variadic function.
//
- // Similarly, per 3.2.3. of the AVX512 draft, 512-bits ("named") args are
- // split into eight eightbyte chunks, one SSE and seven SSEUP.
+ // Similarly, per 3.2.3. of the AVX512 draft, 512-bits ("named") args
+ // are split into eight eightbyte chunks, one SSE and seven SSEUP.
Lo = SSE;
Hi = SSEUp;
}
@@ -2124,20 +2609,25 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
// contains a single 256-bit element. Since Lo and Hi logic isn't extended
// to work for sizes wider than 128, early check and fallback to memory.
//
+ // Note: There is a bug here, the assumption is that this is returning
+ // "Memory" but actually it is returning "NoClass" because Current is set to
+ // NoClass above. Fixed in the Clang 24 implementation.
if (Size > 128 &&
(Size != EltSize || Size > getNativeVectorSizeForAVXABI(AVXLevel)))
return;
- for (uint64_t i=0, Offset=OffsetBase; i<ArraySize; ++i, Offset += EltSize) {
- Class FieldLo, FieldHi;
- classify(AT->getElementType(), Offset, FieldLo, FieldHi, isNamedArg);
- Lo = merge(Lo, FieldLo);
- Hi = merge(Hi, FieldHi);
+ for (uint64_t i = 0, Offset = OffsetBase; i < ArraySize;
+ ++i, Offset += EltSize) {
+ SmallVector<Class> FieldEightBytes = {NoClass, NoClass};
+ classifyClang23(AT->getElementType(), Offset, FieldEightBytes, isNamedArg,
+ IsRegCall);
+ Lo = merge(Lo, FieldEightBytes[0]);
+ Hi = merge(Hi, FieldEightBytes[1]);
if (Lo == Memory || Hi == Memory)
break;
}
- postMerge(Size, Lo, Hi);
+ PostMerge(Size);
assert((Hi != SSEUp || Lo == SSE) && "Invalid SSEUp array classification.");
return;
}
@@ -2173,27 +2663,33 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
assert(!I.isVirtual() && !I.getType()->isDependentType() &&
"Unexpected base class!");
const auto *Base = I.getType()->castAsCXXRecordDecl();
+
// Classify this field.
//
// AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate exceeds a
- // single eightbyte, each is classified separately. Each eightbyte gets
- // initialized to class NO_CLASS.
- Class FieldLo, FieldHi;
+ // single eightbyte, each is classified separately. Each eightbyte
+ // gets initialized to class NO_CLASS.
+ SmallVector<Class> FieldEightBytes = {NoClass, NoClass};
uint64_t Offset =
- OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base));
- classify(I.getType(), Offset, FieldLo, FieldHi, isNamedArg);
- Lo = merge(Lo, FieldLo);
- Hi = merge(Hi, FieldHi);
+ OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base));
+ classifyClang23(I.getType(), Offset, FieldEightBytes, isNamedArg,
+ IsRegCall);
+ // Note: There is a bug here for types larger than 128bits. Doesn't
+ // properly implement the SYSV classification algorithm. Fixed in
+ // Clang 24 implementation.
+ Lo = merge(Lo, FieldEightBytes[0]);
+ Hi = merge(Hi, FieldEightBytes[1]);
if (returnCXXRecordGreaterThan128InMem() &&
!isEmptyRecord(getContext(), I.getType(), true) &&
(Size > 128 && (Size != getContext().getTypeSize(I.getType()) ||
Size > getNativeVectorSizeForAVXABI(AVXLevel)))) {
- // The only case a 256(or 512)-bit wide vector could be used to return
- // is when CXX record contains a single 256(or 512)-bit element.
+ // The only case a 256(or 512)-bit wide vector could be used to
+ // return is when CXX record contains a single 256(or 512)-bit
+ // element.
Lo = Memory;
}
if (Lo == Memory || Hi == Memory) {
- postMerge(Size, Lo, Hi);
+ PostMerge(Size);
return;
}
}
@@ -2211,7 +2707,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
bool IsUnion = RT->isUnionType() && !UseClang11Compat;
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
- i != e; ++i, ++idx) {
+ i != e; ++i, ++idx) {
uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx);
bool BitField = i->isBitField();
@@ -2225,11 +2721,12 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
continue;
// AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger than
- // eight eightbytes, or it contains unaligned fields, it has class MEMORY.
+ // eight eightbytes, or it contains unaligned fields, it has class
+ // MEMORY.
//
- // The only case a 256-bit or a 512-bit wide vector could be used is when
- // the struct contains a single 256-bit or 512-bit element. Early check
- // and fallback to memory.
+ // The only case a 256-bit or a 512-bit wide vector could be used is
+ // when the struct contains a single 256-bit or 512-bit element. Early
+ // check and fallback to memory.
//
// FIXME: Extended the Lo and Hi logic properly to work for size wider
// than 128.
@@ -2237,7 +2734,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
((!IsUnion && Size != getContext().getTypeSize(i->getType())) ||
Size > getNativeVectorSizeForAVXABI(AVXLevel))) {
Lo = Memory;
- postMerge(Size, Lo, Hi);
+ PostMerge(Size);
return;
}
@@ -2246,7 +2743,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
// Note, skip this test for bit-fields, see below.
if (!BitField && IsInMemory) {
Lo = Memory;
- postMerge(Size, Lo, Hi);
+ PostMerge(Size);
return;
}
@@ -2256,7 +2753,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
// exceeds a single eightbyte, each is classified
// separately. Each eightbyte gets initialized to class
// NO_CLASS.
- Class FieldLo, FieldHi;
+ SmallVector<Class> FieldEightBytes = {NoClass, NoClass};
// Bit-fields require special handling, they do not force the
// structure to be passed in memory even if unaligned, and
@@ -2272,28 +2769,33 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
if (EB_Lo) {
assert(EB_Hi == EB_Lo && "Invalid classification, type > 16 bytes.");
- FieldLo = NoClass;
- FieldHi = Integer;
+ FieldEightBytes[0] = NoClass;
+ FieldEightBytes[1] = Integer;
} else {
- FieldLo = Integer;
- FieldHi = EB_Hi ? Integer : NoClass;
+ FieldEightBytes[0] = Integer;
+ FieldEightBytes[1] = EB_Hi ? Integer : NoClass;
}
} else
- classify(i->getType(), Offset, FieldLo, FieldHi, isNamedArg);
- Lo = merge(Lo, FieldLo);
- Hi = merge(Hi, FieldHi);
+ classifyClang23(i->getType(), Offset, FieldEightBytes, isNamedArg,
+ IsRegCall);
+ Lo = merge(Lo, FieldEightBytes[0]);
+ Hi = merge(Hi, FieldEightBytes[1]);
if (Lo == Memory || Hi == Memory)
break;
}
- postMerge(Size, Lo, Hi);
+ PostMerge(Size);
}
}
ABIArgInfo X86_64ABIInfo::getIndirectReturnResult(QualType Ty) const {
// If this is a scalar LLVM value then assume LLVM will pass it in the right
// place naturally.
- if (!isAggregateTypeForABI(Ty)) {
+ // The legacy classifier relied on this fallback for ABI compatibility, even
+ // for vector types which it had classified as memory. The Clang 24
+ // classifier instead expects a memory classification to remain indirect.
+ if (!isAggregateTypeForABI(Ty) &&
+ (useLegacyClassificationAlgorithm() || !IsIllegalVectorType(Ty))) {
// Treat an enum type as its underlying type.
if (const auto *ED = Ty->getAsEnumDecl())
Ty = ED->getIntegerType();
@@ -2310,6 +2812,13 @@ ABIArgInfo X86_64ABIInfo::getIndirectReturnResult(QualType Ty) const {
}
bool X86_64ABIInfo::IsIllegalVectorType(QualType Ty) const {
+ if (const AtomicType *AT = Ty->getAs<AtomicType>()) {
+ // Preserve the pre-Clang 24 direct lowering of atomic vector types.
+ if (useLegacyClassificationAlgorithm())
+ return false;
+ Ty = AT->getValueType();
+ }
+
if (const VectorType *VecTy = Ty->getAs<VectorType>()) {
uint64_t Size = getContext().getTypeSize(VecTy);
unsigned LargestVector = getNativeVectorSizeForAVXABI(AVXLevel);
@@ -2332,9 +2841,9 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty,
//
// This assumption is optimistic, as there could be free registers available
// when we need to pass this argument in memory, and LLVM could try to pass
- // the argument in the free register. This does not seem to happen currently,
- // but this code would be much safer if we could mark the argument with
- // 'onstack'. See PR12193.
+ // the argument in the free register. This does not seem to happen
+ // currently, but this code would be much safer if we could mark the
+ // argument with 'onstack'. See PR12193.
if (!isAggregateTypeForABI(Ty) && !IsIllegalVectorType(Ty) &&
!Ty->isBitIntType()) {
// Treat an enum type as its underlying type.
@@ -2360,18 +2869,18 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty,
// We do this by coercing the value into a scalar type which the backend can
// handle naturally (i.e., without using byval).
//
- // For simplicity, we currently only do this when we have exhausted all of the
- // free integer registers. Doing this when there are free integer registers
- // would require more care, as we would have to ensure that the coerced value
- // did not claim the unused register. That would require either reording the
- // arguments to the function (so that any subsequent inreg values came first),
- // or only doing this optimization when there were no following arguments that
- // might be inreg.
+ // For simplicity, we currently only do this when we have exhausted all of
+ // the free integer registers. Doing this when there are free integer
+ // registers would require more care, as we would have to ensure that the
+ // coerced value did not claim the unused register. That would require
+ // either reording the arguments to the function (so that any subsequent
+ // inreg values came first), or only doing this optimization when there were
+ // no following arguments that might be inreg.
//
// We currently expect it to be rare (particularly in well written code) for
// arguments to be passed on the stack when there are still free integer
- // registers available (this would typically imply large structs being passed
- // by value), so this seems like a fair tradeoff for now.
+ // registers available (this would typically imply large structs being
+ // passed by value), so this seems like a fair tradeoff for now.
//
// We can revisit this if the backend grows support for 'onstack' parameter
// attributes. See PR12193.
@@ -2381,8 +2890,8 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty,
// If this type fits in an eightbyte, coerce it into the matching integral
// type, which will end up on the stack (with alignment 8).
if (Align == 8 && Size <= 64)
- return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(),
- Size));
+ return ABIArgInfo::getDirect(
+ llvm::IntegerType::get(getVMContext(), Size));
}
return ABIArgInfo::getIndirect(CharUnits::fromQuantity(Align),
@@ -2419,7 +2928,6 @@ llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const {
uint64_t Size = getContext().getTypeSize(Ty);
assert((Size == 128 || Size == 256 || Size == 512) && "Invalid type found!");
-
// Return a LLVM IR vector type based on the size of 'Ty'.
return llvm::FixedVectorType::get(llvm::Type::getDoubleTy(getVMContext()),
Size / 64);
@@ -2427,9 +2935,10 @@ llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const {
/// BitsContainNoUserData - Return true if the specified [start,end) bit range
/// is known to either be off the end of the specified type or being in
-/// alignment padding. The user type specified is known to be at most 128 bits
-/// in size, and have passed through X86_64ABIInfo::classify with a successful
-/// classification that put one of the two halves in the INTEGER class.
+/// alignment padding. The user type specified is known to be at most 128
+/// bits in size, and have passed through X86_64ABIInfo::classify with a
+/// successful classification that put one of the two halves in the INTEGER
+/// class.
///
/// It is conservatively correct to return false.
static bool BitsContainNoUserData(QualType Ty, unsigned StartBit,
@@ -2445,15 +2954,17 @@ static bool BitsContainNoUserData(QualType Ty, unsigned StartBit,
unsigned EltSize = (unsigned)Context.getTypeSize(AT->getElementType());
unsigned NumElts = (unsigned)AT->getZExtSize();
- // Check each element to see if the element overlaps with the queried range.
+ // Check each element to see if the element overlaps with the queried
+ // range.
for (unsigned i = 0; i != NumElts; ++i) {
// If the element is after the span we care about, then we're done..
- unsigned EltOffset = i*EltSize;
- if (EltOffset >= EndBit) break;
+ unsigned EltOffset = i * EltSize;
+ if (EltOffset >= EndBit)
+ break;
- unsigned EltStart = EltOffset < StartBit ? StartBit-EltOffset :0;
+ unsigned EltStart = EltOffset < StartBit ? StartBit - EltOffset : 0;
if (!BitsContainNoUserData(AT->getElementType(), EltStart,
- EndBit-EltOffset, Context))
+ EndBit - EltOffset, Context))
return false;
}
// If it overlaps no elements, then it is safe to process as padding.
@@ -2472,16 +2983,17 @@ static bool BitsContainNoUserData(QualType Ty, unsigned StartBit,
// If the base is after the span we care about, ignore it.
unsigned BaseOffset = Context.toBits(Layout.getBaseClassOffset(Base));
- if (BaseOffset >= EndBit) continue;
+ if (BaseOffset >= EndBit)
+ continue;
- unsigned BaseStart = BaseOffset < StartBit ? StartBit-BaseOffset :0;
- if (!BitsContainNoUserData(I.getType(), BaseStart,
- EndBit-BaseOffset, Context))
+ unsigned BaseStart = BaseOffset < StartBit ? StartBit - BaseOffset : 0;
+ if (!BitsContainNoUserData(I.getType(), BaseStart, EndBit - BaseOffset,
+ Context))
return false;
}
}
- // Verify that no field has data that overlaps the region of interest. Yes
+ // Verify that no field has data that overlaps the region of interest. Yes
// this could be sped up a lot by being smarter about queried fields,
// however we're only looking at structs up to 16 bytes, so we don't care
// much.
@@ -2491,10 +3003,11 @@ static bool BitsContainNoUserData(QualType Ty, unsigned StartBit,
unsigned FieldOffset = (unsigned)Layout.getFieldOffset(idx);
// If we found a field after the region we care about, then we're done.
- if (FieldOffset >= EndBit) break;
+ if (FieldOffset >= EndBit)
+ break;
- unsigned FieldStart = FieldOffset < StartBit ? StartBit-FieldOffset :0;
- if (!BitsContainNoUserData(i->getType(), FieldStart, EndBit-FieldOffset,
+ unsigned FieldStart = FieldOffset < StartBit ? StartBit - FieldOffset : 0;
+ if (!BitsContainNoUserData(i->getType(), FieldStart, EndBit - FieldOffset,
Context))
return false;
}
@@ -2535,14 +3048,27 @@ static llvm::Type *getFPTypeAtOffset(llvm::Type *IRType, unsigned IROffset,
return nullptr;
}
-/// GetSSETypeAtOffset - Return a type that will be passed by the backend in the
-/// low 8 bytes of an XMM register, corresponding to the SSE class.
-llvm::Type *X86_64ABIInfo::
-GetSSETypeAtOffset(llvm::Type *IRType, unsigned IROffset,
- QualType SourceTy, unsigned SourceOffset) const {
+/// GetSSETypeAtOffset - Return a type that will be passed by the backend in
+/// the low 8 bytes of an XMM register, corresponding to the SSE class.
+llvm::Type *X86_64ABIInfo::GetSSETypeAtOffset(llvm::Type *IRType,
+ unsigned IROffset,
+ QualType SourceTy,
+ unsigned SourceOffset) const {
const llvm::DataLayout &TD = getDataLayout();
unsigned SourceSize =
(unsigned)getContext().getTypeSize(SourceTy) / 8 - SourceOffset;
+
+ // Keep 16-bit FP vector lanes visible when they fit in one SSE eightbyte.
+ // Reinterpreting them as double would make attributes like nofpclass describe
+ // carrier bits rather than the source vector elements.
+ if (IROffset == 0 && SourceOffset == 0) {
+ if (auto *VTy = dyn_cast<llvm::FixedVectorType>(IRType)) {
+ llvm::Type *EltTy = VTy->getElementType();
+ if (EltTy->is16bitFPTy() && TD.getTypeAllocSize(VTy) <= 8)
+ return VTy;
+ }
+ }
+
llvm::Type *T0 = getFPTypeAtOffset(IRType, IROffset, TD);
if (!T0 || T0->isDoubleTy())
return llvm::Type::getDoubleTy(getVMContext());
@@ -2551,7 +3077,7 @@ GetSSETypeAtOffset(llvm::Type *IRType, unsigned IROffset,
llvm::Type *T1 = nullptr;
unsigned T0Size = TD.getTypeAllocSize(T0);
if (SourceSize > T0Size)
- T1 = getFPTypeAtOffset(IRType, IROffset + T0Size, TD);
+ T1 = getFPTypeAtOffset(IRType, IROffset + T0Size, TD);
if (T1 == nullptr) {
// Check if IRType is a half/bfloat + float. float type will be in IROffset+4 due
// to its alignment.
@@ -2712,8 +3238,20 @@ GetX86_64ByValArgumentPair(llvm::Type *Lo, llvm::Type *Hi,
ABIArgInfo X86_64ABIInfo::classifyReturnType(QualType RetTy) const {
// AMD64-ABI 3.2.3p4: Rule 1. Classify the return type with the
// classification algorithm.
- X86_64ABIInfo::Class Lo, Hi;
- classify(RetTy, 0, Lo, Hi, /*isNamedArg*/ true);
+
+ SmallVector<X86_64ABIInfo::Class> EightBytes;
+ classify(RetTy, 0, EightBytes, /*isNamedArg*/ true);
+
+ X86_64ABIInfo::Class Lo = NoClass;
+ X86_64ABIInfo::Class Hi = NoClass;
+
+ if (EightBytes.size() > 0) {
+ Lo = EightBytes[0];
+ }
+
+ if (EightBytes.size() > 1) {
+ Hi = EightBytes[1];
+ }
// Check some invariants.
assert((Hi != Memory || Lo == Memory) && "Invalid memory classification.");
@@ -2848,8 +3386,19 @@ X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned freeIntRegs,
bool isNamedArg, bool IsRegCall) const {
Ty = useFirstFieldIfTransparentUnion(Ty);
- X86_64ABIInfo::Class Lo, Hi;
- classify(Ty, 0, Lo, Hi, isNamedArg, IsRegCall);
+ SmallVector<X86_64ABIInfo::Class> EightBytes;
+ classify(Ty, 0, EightBytes, isNamedArg, IsRegCall);
+
+ X86_64ABIInfo::Class Lo = NoClass;
+ X86_64ABIInfo::Class Hi = NoClass;
+
+ if (EightBytes.size() > 0) {
+ Lo = EightBytes[0];
+ }
+
+ if (EightBytes.size() > 1) {
+ Hi = EightBytes[1];
+ }
// Check some invariants.
// FIXME: Enforce these by construction.
diff --git a/clang/test/CodeGen/X86/x86-illegal-vector.c b/clang/test/CodeGen/X86/x86-illegal-vector.c
new file mode 100644
index 0000000000000..abb863f28d945
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86-illegal-vector.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,MEMRET256,MEMRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,REGRET256,MEMRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+
+#define __MM_MALLOC_H
+#include <x86intrin.h>
+
+// REGRET128: define{{.*}} <4 x float> @get2()
+__m128 get2() { return (__m128){5, 6}; }
+
+// MEMRET256: define{{.*}} void @get4(ptr{{.*}} sret(<8 x float>) align 32 %{{.*}})
+// REGRET256: define{{.*}} <8 x float> @get4()
+__m256 get4() { return (__m256){7, 8, 9, 10}; }
+
+// MEMRET512: define{{.*}} void @get8(ptr{{.*}} sret(<16 x float>) align 64 %{{.*}})
+// REGRET512: define{{.*}} <16 x float> @get8()
+__m512 get8() { return (__m512){7, 8, 9, 10, 1, 2, 3, 4}; }
diff --git a/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c b/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
new file mode 100644
index 0000000000000..66f0fec6ee247
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fclang-abi-compat=22 -emit-llvm -o - %s | FileCheck %s --check-prefix=LEGACY
+
+typedef _Float16 v1hf __attribute__((vector_size(2)));
+typedef _Float16 v2hf __attribute__((vector_size(4)));
+typedef _Float16 v4hf __attribute__((vector_size(8)));
+typedef float v1sf __attribute__((vector_size(4)));
+typedef float v2sf __attribute__((vector_size(8)));
+typedef double v1df __attribute__((vector_size(8)));
+typedef int v1si __attribute__((vector_size(4)));
+
+// Single-element floating-point vectors are passed in memory for GCC
+// compatibility.
+// CHECK-LABEL: define{{.*}} half @take_v1hf(ptr noundef byval(<1 x half>) align 8
+// LEGACY-LABEL: define{{.*}} half @take_v1hf(i16 noundef
+_Float16 take_v1hf(v1hf x) { return x[0]; }
+
+// CHECK-LABEL: define{{.*}} float @take_v1sf(ptr noundef byval(<1 x float>) align 8
+// LEGACY-LABEL: define{{.*}} float @take_v1sf(i32 noundef
+float take_v1sf(v1sf x) { return x[0]; }
+
+// CHECK-LABEL: define{{.*}} double @take_v1df(ptr noundef byval(<1 x double>) align 8
+// LEGACY-LABEL: define{{.*}} double @take_v1df(ptr noundef byval(<1 x double>) align 8
+double take_v1df(v1df x) { return x[0]; }
+
+// Single-element floating-point vector returns use memory too.
+// CHECK-LABEL: define{{.*}} void @return_v1hf(
+// CHECK-SAME: ptr {{.*}}sret(<1 x half>) align 2 %agg.result,
+// CHECK-SAME: ptr noundef byval(<1 x half>) align 8
+// LEGACY-LABEL: define{{.*}} i16 @return_v1hf(i16 noundef
+v1hf return_v1hf(v1hf x) { return x; }
+
+// CHECK-LABEL: define{{.*}} void @return_v1sf(
+// CHECK-SAME: ptr {{.*}}sret(<1 x float>) align 4 %agg.result,
+// CHECK-SAME: ptr noundef byval(<1 x float>) align 8
+// LEGACY-LABEL: define{{.*}} i32 @return_v1sf(i32 noundef
+v1sf return_v1sf(v1sf x) { return x; }
+
+// CHECK-LABEL: define{{.*}} void @return_v1df(
+// CHECK-SAME: ptr {{.*}}sret(<1 x double>) align 8 %agg.result,
+// CHECK-SAME: ptr noundef byval(<1 x double>) align 8
+// LEGACY-LABEL: define{{.*}} <1 x double> @return_v1df(
+// LEGACY-SAME: ptr noundef byval(<1 x double>) align 8
+v1df return_v1df(v1df x) { return x; }
+
+// Multi-element floating-point vectors are classified as SSE.
+// CHECK-LABEL: define{{.*}} half @take_v2hf(<2 x half> noundef
+_Float16 take_v2hf(v2hf x) { return x[0]; }
+
+// CHECK-LABEL: define{{.*}} half @take_v4hf(<4 x half> noundef
+_Float16 take_v4hf(v4hf x) { return x[0]; }
+
+// CHECK-LABEL: define{{.*}} float @take_v2sf(double noundef
+float take_v2sf(v2sf x) { return x[0]; }
+
+// Small integer vectors stay INTEGER.
+// CHECK-LABEL: define{{.*}} i32 @take_v1si(i32 noundef
+int take_v1si(v1si x) { return x[0]; }
diff --git a/clang/test/CodeGen/X86/x86-vec-i128.c b/clang/test/CodeGen/X86/x86-vec-i128.c
index ee58cb92da6b1..102db38f13a9b 100644
--- a/clang/test/CodeGen/X86/x86-vec-i128.c
+++ b/clang/test/CodeGen/X86/x86-vec-i128.c
@@ -1,16 +1,16 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,MEM256ALIGN32,MEM512ALIGN64
// RUN: %clang_cc1 -triple x86_64-netbsd %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,MEM256ALIGN32,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,MEM256ALIGN16,MEM512ALIGN16
-// RUN: %clang_cc1 -triple x86_64-scei-ps4 %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,MEM256ALIGN32,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,MEM256ALIGN32,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - -fclang-abi-compat=9 | FileCheck %s --check-prefixes=CLANG9ABI128,MEM256ALIGN32,MEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,LEGACYMEM256ALIGN16,LEGACYMEM512ALIGN16
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,LEGACYMEM256ALIGN32,LEGACYMEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,LEGACYMEM256ALIGN32,LEGACYMEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - -fclang-abi-compat=9 | FileCheck %s --check-prefixes=CLANG9ABI128,LEGACYMEM256ALIGN32,LEGACYMEM512ALIGN64
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,CLANG10ABI256,MEM512ALIGN64
// RUN: %clang_cc1 -triple x86_64-netbsd %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,CLANG10ABI256,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,MEM512ALIGN32
-// RUN: %clang_cc1 -triple x86_64-scei-ps4 %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,MEM512ALIGN64
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - -fclang-abi-compat=9 | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,MEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,LEGACYMEM512ALIGN32
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,LEGACYMEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,LEGACYMEM512ALIGN64
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - -fclang-abi-compat=9 | FileCheck %s --check-prefixes=CLANG9ABI128,CLANG9ABI256,LEGACYMEM512ALIGN64
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,CLANG10ABI256,CLANG10ABI512
// RUN: %clang_cc1 -triple x86_64-netbsd %s -target-feature +avx512f -emit-llvm -o - | FileCheck %s --check-prefixes=CLANG10ABI128,CLANG10ABI256,CLANG10ABI512
@@ -32,8 +32,9 @@ typedef unsigned long long v32u64 __attribute__((vector_size(32)));
typedef unsigned __int128 v32u128 __attribute__((vector_size(32)));
v32u64 test_v32u128(v32u64 a, v32u128 b) {
-// MEM256ALIGN16: define{{.*}} <4 x i64> @test_v32u128(ptr noundef byval(<4 x i64>) align 16 %{{.*}}, ptr noundef byval(<2 x i128>) align 16 %{{.*}})
-// MEM256ALIGN32: define{{.*}} <4 x i64> @test_v32u128(ptr noundef byval(<4 x i64>) align 32 %{{.*}}, ptr noundef byval(<2 x i128>) align 32 %{{.*}})
+// LEGACYMEM256ALIGN16: define{{.*}} <4 x i64> @test_v32u128(ptr noundef byval(<4 x i64>) align 16 %{{.*}}, ptr noundef byval(<2 x i128>) align 16 %{{.*}})
+// MEM256ALIGN32: define{{.*}} void @test_v32u128(ptr dead_on_unwind noalias writable sret(<4 x i64>) align 32 %{{.*}}, ptr noundef byval(<4 x i64>) align 32 %{{.*}}, ptr noundef byval(<2 x i128>) align 32 %{{.*}})
+// LEGACYMEM256ALIGN32: define{{.*}} <4 x i64> @test_v32u128(ptr noundef byval(<4 x i64>) align 32 %{{.*}}, ptr noundef byval(<2 x i128>) align 32 %{{.*}})
// CLANG10ABI256: define{{.*}} <4 x i64> @test_v32u128(<4 x i64> noundef %{{.*}}, ptr noundef byval(<2 x i128>) align 32 %{{.*}})
// CLANG9ABI256: define{{.*}} <4 x i64> @test_v32u128(<4 x i64> noundef %{{.*}}, <2 x i128> noundef %{{.*}})
return a + (v32u64)b;
@@ -43,9 +44,10 @@ typedef unsigned long long v64u64 __attribute__((vector_size(64)));
typedef unsigned __int128 v64u128 __attribute__((vector_size(64)));
v64u64 test_v64u128(v64u64 a, v64u128 b) {
-// MEM512ALIGN16: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 16 %{{.*}}, ptr noundef byval(<4 x i128>) align 16 %{{.*}})
-// MEM512ALIGN32: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 32 %{{.*}}, ptr noundef byval(<4 x i128>) align 32 %{{.*}})
-// MEM512ALIGN64: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 64 %{{.*}}, ptr noundef byval(<4 x i128>) align 64 %{{.*}})
+// LEGACYMEM512ALIGN16: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 16 %{{.*}}, ptr noundef byval(<4 x i128>) align 16 %{{.*}})
+// LEGACYMEM512ALIGN32: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 32 %{{.*}}, ptr noundef byval(<4 x i128>) align 32 %{{.*}})
+// MEM512ALIGN64: define{{.*}} void @test_v64u128(ptr dead_on_unwind noalias writable sret(<8 x i64>) align 64 %{{.*}}, ptr noundef byval(<8 x i64>) align 64 %{{.*}}, ptr noundef byval(<4 x i128>) align 64 %{{.*}})
+// LEGACYMEM512ALIGN64: define{{.*}} <8 x i64> @test_v64u128(ptr noundef byval(<8 x i64>) align 64 %{{.*}}, ptr noundef byval(<4 x i128>) align 64 %{{.*}})
// CLANG10ABI512: define{{.*}} <8 x i64> @test_v64u128(<8 x i64> noundef %{{.*}}, ptr noundef byval(<4 x i128>) align 64 %{{.*}})
// CLANG9ABI512: define{{.*}} <8 x i64> @test_v64u128(<8 x i64> noundef %{{.*}}, <4 x i128> noundef %{{.*}})
return a + (v64u64)b;
diff --git a/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c b/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
new file mode 100644
index 0000000000000..6d0bf403879fb
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-feature +avx %s -emit-llvm -o - | FileCheck %s --check-prefix=AVX
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-feature +avx -fclang-abi-compat=22 %s -emit-llvm -o - | FileCheck %s --check-prefix=LEGACY
+
+struct F3 {
+ float a[3];
+};
+
+typedef _Atomic(float) atomic_float;
+typedef _Atomic(struct F3) atomic_f3;
+typedef _Float16 v1hf __attribute__((vector_size(2)));
+typedef __bf16 v1bf __attribute__((vector_size(2)));
+typedef float v1sf __attribute__((vector_size(4)));
+typedef double v1df __attribute__((vector_size(8)));
+typedef __int128 v2ti __attribute__((vector_size(32)));
+typedef _Atomic(v1hf) atomic_v1hf;
+typedef _Atomic(v1bf) atomic_v1bf;
+typedef _Atomic(v1sf) atomic_v1sf;
+typedef _Atomic(v1df) atomic_v1df;
+typedef _Atomic(v2ti) atomic_v2ti;
+
+// CHECK-LABEL: define dso_local void @take_float(
+// CHECK-SAME: float %x)
+void take_float(atomic_float x) {}
+
+// CHECK-LABEL: define dso_local float @ret_float(
+// CHECK-SAME: float %x)
+atomic_float ret_float(atomic_float x) {
+ return x;
+}
+
+// CHECK-LABEL: define dso_local void @take_f3(
+// CHECK-SAME: ptr noundef byval({ %struct.F3, [4 x i8] }) align 16 %x)
+void take_f3(atomic_f3 x) {}
+
+// CHECK-LABEL: define dso_local void @ret_f3(
+// CHECK-SAME: ptr {{.*}}sret({ %struct.F3, [4 x i8] }) align 16 %agg.result,
+// CHECK-SAME: ptr noundef byval({ %struct.F3, [4 x i8] }) align 16 %x)
+atomic_f3 ret_f3(atomic_f3 x) {
+ return x;
+}
+
+// AVX-LABEL: define dso_local void @ret_v1hf(
+// AVX-SAME: ptr {{.*}}sret(<1 x half>) align 2 %agg.result,
+// AVX-SAME: ptr noundef byval(<1 x half>) align 8 %{{.*}})
+atomic_v1hf ret_v1hf(atomic_v1hf x) {
+ return x;
+}
+
+// AVX-LABEL: define dso_local void @ret_v1bf(
+// AVX-SAME: ptr {{.*}}sret(<1 x bfloat>) align 2 %agg.result,
+// AVX-SAME: ptr noundef byval(<1 x bfloat>) align 8 %{{.*}})
+atomic_v1bf ret_v1bf(atomic_v1bf x) {
+ return x;
+}
+
+// AVX-LABEL: define dso_local void @take_v1sf(
+// AVX-SAME: ptr noundef byval(<1 x float>) align 8 %{{.*}})
+// LEGACY-LABEL: define dso_local void @take_v1sf(
+// LEGACY-SAME: <1 x float> %x)
+void take_v1sf(atomic_v1sf x) {}
+
+// AVX-LABEL: define dso_local void @ret_v1sf(
+// AVX-SAME: ptr {{.*}}sret(<1 x float>) align 4 %agg.result,
+// AVX-SAME: ptr noundef byval(<1 x float>) align 8 %{{.*}})
+// LEGACY-LABEL: define dso_local <1 x float> @ret_v1sf(
+// LEGACY-SAME: <1 x float> %x)
+atomic_v1sf ret_v1sf(atomic_v1sf x) {
+ return x;
+}
+
+// AVX-LABEL: define dso_local void @ret_v1df(
+// AVX-SAME: ptr {{.*}}sret(<1 x double>) align 8 %agg.result,
+// AVX-SAME: ptr noundef byval(<1 x double>) align 8 %{{.*}})
+atomic_v1df ret_v1df(atomic_v1df x) {
+ return x;
+}
+
+// AVX-LABEL: define dso_local void @take_v2ti(
+// AVX-SAME: ptr noundef byval(<2 x i128>) align 32 %{{.*}})
+// LEGACY-LABEL: define dso_local void @take_v2ti(
+// LEGACY-SAME: <2 x i128> %x)
+void take_v2ti(atomic_v2ti x) {}
+
+// AVX-LABEL: define dso_local void @ret_v2ti(
+// AVX-SAME: ptr {{.*}}sret(<2 x i128>) align 32 %agg.result,
+// AVX-SAME: ptr noundef byval(<2 x i128>) align 32 %{{.*}})
+// LEGACY-LABEL: define dso_local <2 x i128> @ret_v2ti(
+// LEGACY-SAME: <2 x i128> %x)
+atomic_v2ti ret_v2ti(atomic_v2ti x) {
+ return x;
+}
diff --git a/clang/test/CodeGen/builtin-masked.c b/clang/test/CodeGen/builtin-masked.c
index c1ae989d50411..93417017a3939 100644
--- a/clang/test/CodeGen/builtin-masked.c
+++ b/clang/test/CodeGen/builtin-masked.c
@@ -5,8 +5,8 @@ typedef int v8i __attribute__((ext_vector_type(8)));
typedef _Bool v8b __attribute__((ext_vector_type(8)));
typedef int gv8i [[gnu::vector_size(sizeof(int) * 8)]];
-// CHECK-LABEL: define dso_local <8 x i32> @test_load(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-LABEL: define dso_local void @test_load(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -21,14 +21,17 @@ typedef int gv8i [[gnu::vector_size(sizeof(int) * 8)]];
// CHECK-NEXT: [[TMP1:%.*]] = bitcast i8 [[LOAD_BITS2]] to <8 x i1>
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[MASKED_LOAD:%.*]] = call <8 x i32> @llvm.masked.load.v8i32.p0(ptr align 4 [[TMP2]], <8 x i1> [[TMP1]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_LOAD]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_LOAD]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP3:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP3]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_load(v8b m, int *p) {
return __builtin_masked_load(m, p);
}
-// CHECK-LABEL: define dso_local <8 x i32> @test_load_passthru(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @test_load_passthru(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -47,14 +50,17 @@ v8i test_load(v8b m, int *p) {
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[TMP4:%.*]] = load <8 x i32>, ptr [[T_ADDR]], align 32
// CHECK-NEXT: [[MASKED_LOAD:%.*]] = call <8 x i32> @llvm.masked.load.v8i32.p0(ptr align 4 [[TMP3]], <8 x i1> [[TMP2]], <8 x i32> [[TMP4]])
-// CHECK-NEXT: ret <8 x i32> [[MASKED_LOAD]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_LOAD]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP5:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP5]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_load_passthru(v8b m, int *p, v8i t) {
return __builtin_masked_load(m, p, t);
}
-// CHECK-LABEL: define dso_local <8 x i32> @test_load_expand(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @test_load_expand(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[M_COERCE:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -73,14 +79,17 @@ v8i test_load_passthru(v8b m, int *p, v8i t) {
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[TMP4:%.*]] = load <8 x i32>, ptr [[T_ADDR]], align 32
// CHECK-NEXT: [[MASKED_EXPAND_LOAD:%.*]] = call <8 x i32> @llvm.masked.expandload.v8i32.p0(ptr [[TMP3]], <8 x i1> [[TMP2]], <8 x i32> [[TMP4]])
-// CHECK-NEXT: ret <8 x i32> [[MASKED_EXPAND_LOAD]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_EXPAND_LOAD]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP5:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP5]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_load_expand(v8b m, int *p, v8i t) {
return __builtin_masked_expand_load(m, p, t);
}
// CHECK-LABEL: define dso_local void @test_store(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -106,7 +115,7 @@ void test_store(v8b m, v8i v, int *p) {
}
// CHECK-LABEL: define dso_local void @gtest_store(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -132,7 +141,7 @@ void gtest_store(v8b m, gv8i v, int *p) {
}
// CHECK-LABEL: define dso_local void @test_compress_store(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -158,7 +167,7 @@ void test_compress_store(v8b m, v8i v, int *p) {
}
// CHECK-LABEL: define dso_local void @gtest_compress_store(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -183,8 +192,8 @@ void gtest_compress_store(v8b m, gv8i v, int *p) {
__builtin_masked_compress_store(m, v, p);
}
-// CHECK-LABEL: define dso_local <8 x i32> @test_gather(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @test_gather(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -204,14 +213,17 @@ void gtest_compress_store(v8b m, gv8i v, int *p) {
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[TMP4]], <8 x i32> [[TMP3]]
// CHECK-NEXT: [[MASKED_GATHER:%.*]] = call <8 x i32> @llvm.masked.gather.v8i32.v8p0(<8 x ptr> align 4 [[TMP5]], <8 x i1> [[TMP2]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_GATHER]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_GATHER]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP6:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP6]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_gather(v8b mask, v8i idx, int *ptr) {
return __builtin_masked_gather(mask, idx, ptr);
}
-// CHECK-LABEL: define dso_local <8 x i32> @gtest_gather(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @gtest_gather(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -231,14 +243,17 @@ v8i test_gather(v8b mask, v8i idx, int *ptr) {
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[TMP4]], <8 x i32> [[TMP3]]
// CHECK-NEXT: [[MASKED_GATHER:%.*]] = call <8 x i32> @llvm.masked.gather.v8i32.v8p0(<8 x ptr> align 4 [[TMP5]], <8 x i1> [[TMP2]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_GATHER]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_GATHER]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP6:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP6]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i gtest_gather(v8b mask, gv8i idx, int *ptr) {
return __builtin_masked_gather(mask, idx, ptr);
}
// CHECK-LABEL: define dso_local void @test_scatter(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -269,7 +284,7 @@ void test_scatter(v8b mask, v8i val, v8i idx, int *ptr) {
}
// CHECK-LABEL: define dso_local void @gtest_scatter(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -299,8 +314,8 @@ void gtest_scatter(v8b mask, gv8i val, gv8i idx, int *ptr) {
__builtin_masked_scatter(mask, val, idx, ptr);
}
-// CHECK-LABEL: define dso_local <8 x i32> @test_load_as(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @test_load_as(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[MASK_COERCE:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -315,14 +330,17 @@ void gtest_scatter(v8b mask, gv8i val, gv8i idx, int *ptr) {
// CHECK-NEXT: [[TMP1:%.*]] = bitcast i8 [[LOAD_BITS2]] to <8 x i1>
// CHECK-NEXT: [[TMP2:%.*]] = load ptr addrspace(42), ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[MASKED_LOAD:%.*]] = call <8 x i32> @llvm.masked.load.v8i32.p42(ptr addrspace(42) align 4 [[TMP2]], <8 x i1> [[TMP1]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_LOAD]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_LOAD]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP3:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP3]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_load_as(v8b mask, int __attribute__((address_space(42))) * ptr) {
return __builtin_masked_load(mask, ptr);
}
// CHECK-LABEL: define dso_local void @test_store_as(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[P:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -348,7 +366,7 @@ void test_store_as(v8b m, v8i v, int __attribute__((address_space(42))) *p) {
}
// CHECK-LABEL: define dso_local void @gtest_store_as(
-// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[P:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[M_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[P:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[M:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[M_ADDR:%.*]] = alloca i8, align 1
@@ -373,8 +391,8 @@ void gtest_store_as(v8b m, gv8i v, int __attribute__((address_space(42))) *p) {
__builtin_masked_store(m, v, p);
}
-// CHECK-LABEL: define dso_local <8 x i32> @test_gather_as(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @test_gather_as(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -394,14 +412,17 @@ void gtest_store_as(v8b m, gv8i v, int __attribute__((address_space(42))) *p) {
// CHECK-NEXT: [[TMP4:%.*]] = load ptr addrspace(42), ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr addrspace(42) [[TMP4]], <8 x i32> [[TMP3]]
// CHECK-NEXT: [[MASKED_GATHER:%.*]] = call <8 x i32> @llvm.masked.gather.v8i32.v8p42(<8 x ptr addrspace(42)> align 4 [[TMP5]], <8 x i1> [[TMP2]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_GATHER]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_GATHER]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP6:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP6]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i test_gather_as(v8b mask, v8i idx, int __attribute__((address_space(42))) *ptr) {
return __builtin_masked_gather(mask, idx, ptr);
}
-// CHECK-LABEL: define dso_local <8 x i32> @gtest_gather_as(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @gtest_gather_as(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<8 x i32>) align 32 [[AGG_RESULT:%.*]], i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -421,14 +442,17 @@ v8i test_gather_as(v8b mask, v8i idx, int __attribute__((address_space(42))) *pt
// CHECK-NEXT: [[TMP4:%.*]] = load ptr addrspace(42), ptr [[PTR_ADDR]], align 8
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr addrspace(42) [[TMP4]], <8 x i32> [[TMP3]]
// CHECK-NEXT: [[MASKED_GATHER:%.*]] = call <8 x i32> @llvm.masked.gather.v8i32.v8p42(<8 x ptr addrspace(42)> align 4 [[TMP5]], <8 x i1> [[TMP2]], <8 x i32> poison)
-// CHECK-NEXT: ret <8 x i32> [[MASKED_GATHER]]
+// CHECK-NEXT: store <8 x i32> [[MASKED_GATHER]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: [[TMP6:%.*]] = load <8 x i32>, ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: store <8 x i32> [[TMP6]], ptr [[AGG_RESULT]], align 32
+// CHECK-NEXT: ret void
//
v8i gtest_gather_as(v8b mask, gv8i idx, int __attribute__((address_space(42))) *ptr) {
return __builtin_masked_gather(mask, idx, ptr);
}
// CHECK-LABEL: define dso_local void @test_scatter_as(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
@@ -459,7 +483,7 @@ void test_scatter_as(v8b mask, v8i val, v8i idx, int __attribute__((address_spac
}
// CHECK-LABEL: define dso_local void @gtest_scatter_as(
-// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR3]] {
+// CHECK-SAME: i8 noundef [[MASK_COERCE:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]], ptr noundef byval(<8 x i32>) align 32 [[TMP1:%.*]], ptr addrspace(42) noundef [[PTR:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[MASK:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[MASK_ADDR:%.*]] = alloca i8, align 1
diff --git a/clang/test/CodeGen/builtin-maximumnum-minimumnum.c b/clang/test/CodeGen/builtin-maximumnum-minimumnum.c
index aa18d9ca217f7..4167a473b0bfa 100644
--- a/clang/test/CodeGen/builtin-maximumnum-minimumnum.c
+++ b/clang/test/CodeGen/builtin-maximumnum-minimumnum.c
@@ -67,8 +67,8 @@ float4 pfmin32(float4 a, float4 b) {
double2 pfmin64(double2 a, double2 b) {
return __builtin_elementwise_minimumnum(a, b);
}
-// CHECK-LABEL: define dso_local noundef <2 x x86_fp80> @_Z7pfmin80Dv2_eS_(
-// CHECK-SAME: ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-LABEL: define dso_local void @_Z7pfmin80Dv2_eS_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<2 x x86_fp80>) align 32 [[AGG_RESULT:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
@@ -79,7 +79,10 @@ double2 pfmin64(double2 a, double2 b) {
// CHECK-NEXT: [[TMP2:%.*]] = load <2 x x86_fp80>, ptr [[A_ADDR]], align 32, !tbaa [[TBAA6]]
// CHECK-NEXT: [[TMP3:%.*]] = load <2 x x86_fp80>, ptr [[B_ADDR]], align 32, !tbaa [[TBAA6]]
// CHECK-NEXT: [[ELT_MINIMUMNUM:%.*]] = call <2 x x86_fp80> @llvm.minimumnum.v2f80(<2 x x86_fp80> [[TMP2]], <2 x x86_fp80> [[TMP3]])
-// CHECK-NEXT: ret <2 x x86_fp80> [[ELT_MINIMUMNUM]]
+// CHECK-NEXT: store <2 x x86_fp80> [[ELT_MINIMUMNUM]], ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: [[TMP4:%.*]] = load <2 x x86_fp80>, ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: store <2 x x86_fp80> [[TMP4]], ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: ret void
//
ldouble2 pfmin80(ldouble2 a, ldouble2 b) {
return __builtin_elementwise_minimumnum(a, b);
@@ -146,8 +149,8 @@ double2 pfmax64(double2 a, double2 b) {
return __builtin_elementwise_maximumnum(a, b);
}
-// CHECK-LABEL: define dso_local noundef <2 x x86_fp80> @_Z7pfmax80Dv2_eS_(
-// CHECK-SAME: ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2]] {
+// CHECK-LABEL: define dso_local void @_Z7pfmax80Dv2_eS_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<2 x x86_fp80>) align 32 [[AGG_RESULT:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
@@ -158,7 +161,10 @@ double2 pfmax64(double2 a, double2 b) {
// CHECK-NEXT: [[TMP2:%.*]] = load <2 x x86_fp80>, ptr [[A_ADDR]], align 32, !tbaa [[TBAA6]]
// CHECK-NEXT: [[TMP3:%.*]] = load <2 x x86_fp80>, ptr [[B_ADDR]], align 32, !tbaa [[TBAA6]]
// CHECK-NEXT: [[ELT_MINIMUMNUM:%.*]] = call <2 x x86_fp80> @llvm.minimumnum.v2f80(<2 x x86_fp80> [[TMP2]], <2 x x86_fp80> [[TMP3]])
-// CHECK-NEXT: ret <2 x x86_fp80> [[ELT_MINIMUMNUM]]
+// CHECK-NEXT: store <2 x x86_fp80> [[ELT_MINIMUMNUM]], ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: [[TMP4:%.*]] = load <2 x x86_fp80>, ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: store <2 x x86_fp80> [[TMP4]], ptr [[AGG_RESULT]], align 32, !tbaa [[TBAA6]]
+// CHECK-NEXT: ret void
//
ldouble2 pfmax80(ldouble2 a, ldouble2 b) {
return __builtin_elementwise_minimumnum(a, b);
diff --git a/clang/test/CodeGen/builtin-maxnum-minnum.c b/clang/test/CodeGen/builtin-maxnum-minnum.c
index d05d43c23bf27..e277852f51a09 100644
--- a/clang/test/CodeGen/builtin-maxnum-minnum.c
+++ b/clang/test/CodeGen/builtin-maxnum-minnum.c
@@ -67,8 +67,8 @@ float4 pfmin32(float4 a, float4 b) {
double2 pfmin64(double2 a, double2 b) {
return __builtin_elementwise_minnum(a, b);
}
-// CHECK-LABEL: define dso_local noundef <2 x x86_fp80> @_Z7pfmin80Dv2_eS_(
-// CHECK-SAME: ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-LABEL: define dso_local void @_Z7pfmin80Dv2_eS_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<2 x x86_fp80>) align 32 [[AGG_RESULT:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
@@ -79,7 +79,10 @@ double2 pfmin64(double2 a, double2 b) {
// CHECK-NEXT: [[TMP2:%.*]] = load <2 x x86_fp80>, ptr [[A_ADDR]], align 32, !tbaa [[CHAR_TBAA6]]
// CHECK-NEXT: [[TMP3:%.*]] = load <2 x x86_fp80>, ptr [[B_ADDR]], align 32, !tbaa [[CHAR_TBAA6]]
// CHECK-NEXT: [[ELT_MINNUM:%.*]] = call <2 x x86_fp80> @llvm.minnum.v2f80(<2 x x86_fp80> [[TMP2]], <2 x x86_fp80> [[TMP3]])
-// CHECK-NEXT: ret <2 x x86_fp80> [[ELT_MINNUM]]
+// CHECK-NEXT: store <2 x x86_fp80> [[ELT_MINNUM]], ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: [[TMP4:%.*]] = load <2 x x86_fp80>, ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: store <2 x x86_fp80> [[TMP4]], ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: ret void
//
ldouble2 pfmin80(ldouble2 a, ldouble2 b) {
return __builtin_elementwise_minnum(a, b);
@@ -146,8 +149,8 @@ double2 pfmax64(double2 a, double2 b) {
return __builtin_elementwise_maxnum(a, b);
}
-// CHECK-LABEL: define dso_local noundef <2 x x86_fp80> @_Z7pfmax80Dv2_eS_(
-// CHECK-SAME: ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2]] {
+// CHECK-LABEL: define dso_local void @_Z7pfmax80Dv2_eS_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<2 x x86_fp80>) align 32 [[AGG_RESULT:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP0:%.*]], ptr noundef byval(<2 x x86_fp80>) align 32 [[TMP1:%.*]]) #[[ATTR2]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <2 x x86_fp80>, align 32
@@ -158,7 +161,10 @@ double2 pfmax64(double2 a, double2 b) {
// CHECK-NEXT: [[TMP2:%.*]] = load <2 x x86_fp80>, ptr [[A_ADDR]], align 32, !tbaa [[CHAR_TBAA6]]
// CHECK-NEXT: [[TMP3:%.*]] = load <2 x x86_fp80>, ptr [[B_ADDR]], align 32, !tbaa [[CHAR_TBAA6]]
// CHECK-NEXT: [[ELT_MINNUM:%.*]] = call <2 x x86_fp80> @llvm.minnum.v2f80(<2 x x86_fp80> [[TMP2]], <2 x x86_fp80> [[TMP3]])
-// CHECK-NEXT: ret <2 x x86_fp80> [[ELT_MINNUM]]
+// CHECK-NEXT: store <2 x x86_fp80> [[ELT_MINNUM]], ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: [[TMP4:%.*]] = load <2 x x86_fp80>, ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: store <2 x x86_fp80> [[TMP4]], ptr [[AGG_RESULT]], align 32, !tbaa [[CHAR_TBAA6]]
+// CHECK-NEXT: ret void
//
ldouble2 pfmax80(ldouble2 a, ldouble2 b) {
return __builtin_elementwise_minnum(a, b);
diff --git a/clang/test/CodeGen/nofpclass.c b/clang/test/CodeGen/nofpclass.c
index eb6932f1f6708..6a69b49693902 100644
--- a/clang/test/CodeGen/nofpclass.c
+++ b/clang/test/CodeGen/nofpclass.c
@@ -213,112 +213,94 @@ float call_extern_func(float a, double b, _Float16 c) {
// CFINITEONLY: Function Attrs: noinline nounwind optnone
// CFINITEONLY-LABEL: define dso_local nofpclass(nan inf) double @call_extern_func_vec
-// CFINITEONLY-SAME: (double noundef nofpclass(nan inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[B:%.*]], i32 noundef [[C_COERCE:%.*]]) #[[ATTR2]] {
+// CFINITEONLY-SAME: (double noundef nofpclass(nan inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[B:%.*]], <2 x half> noundef nofpclass(nan inf) [[C:%.*]]) #[[ATTR2]] {
// CFINITEONLY-NEXT: entry:
// CFINITEONLY-NEXT: [[RETVAL:%.*]] = alloca <2 x float>, align 8
// CFINITEONLY-NEXT: [[A:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[C:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[A_ADDR:%.*]] = alloca <2 x float>, align 8
// CFINITEONLY-NEXT: [[B_ADDR:%.*]] = alloca <2 x double>, align 16
// CFINITEONLY-NEXT: [[C_ADDR:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// CFINITEONLY-NEXT: [[COERCE4:%.*]] = alloca <2 x float>, align 8
+// CFINITEONLY-NEXT: [[COERCE2:%.*]] = alloca <2 x float>, align 8
// CFINITEONLY-NEXT: store double [[A_COERCE]], ptr [[A]], align 8
// CFINITEONLY-NEXT: [[A1:%.*]] = load <2 x float>, ptr [[A]], align 8
-// CFINITEONLY-NEXT: store i32 [[C_COERCE]], ptr [[C]], align 4
-// CFINITEONLY-NEXT: [[C2:%.*]] = load <2 x half>, ptr [[C]], align 4
// CFINITEONLY-NEXT: store <2 x float> [[A1]], ptr [[A_ADDR]], align 8
// CFINITEONLY-NEXT: store <2 x double> [[B]], ptr [[B_ADDR]], align 16
-// CFINITEONLY-NEXT: store <2 x half> [[C2]], ptr [[C_ADDR]], align 4
+// CFINITEONLY-NEXT: store <2 x half> [[C]], ptr [[C_ADDR]], align 4
// CFINITEONLY-NEXT: [[TMP0:%.*]] = load <2 x float>, ptr [[A_ADDR]], align 8
// CFINITEONLY-NEXT: [[TMP1:%.*]] = load <2 x double>, ptr [[B_ADDR]], align 16
// CFINITEONLY-NEXT: [[TMP2:%.*]] = load <2 x half>, ptr [[C_ADDR]], align 4
// CFINITEONLY-NEXT: store <2 x float> [[TMP0]], ptr [[COERCE]], align 8
// CFINITEONLY-NEXT: [[TMP3:%.*]] = load double, ptr [[COERCE]], align 8
-// CFINITEONLY-NEXT: store <2 x half> [[TMP2]], ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[TMP4:%.*]] = load i32, ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) double @extern_func_vec(double noundef nofpclass(nan inf) [[TMP3]], <2 x double> noundef nofpclass(nan inf) [[TMP1]], i32 noundef [[TMP4]])
-// CFINITEONLY-NEXT: store double [[CALL]], ptr [[COERCE4]], align 8
-// CFINITEONLY-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[COERCE4]], align 8
-// CFINITEONLY-NEXT: store <2 x float> [[TMP5]], ptr [[RETVAL]], align 8
-// CFINITEONLY-NEXT: [[TMP6:%.*]] = load double, ptr [[RETVAL]], align 8
-// CFINITEONLY-NEXT: ret double [[TMP6]]
+// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) double @extern_func_vec(double noundef nofpclass(nan inf) [[TMP3]], <2 x double> noundef nofpclass(nan inf) [[TMP1]], <2 x half> noundef nofpclass(nan inf) [[TMP2]])
+// CFINITEONLY-NEXT: store double [[CALL]], ptr [[COERCE2]], align 8
+// CFINITEONLY-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[COERCE2]], align 8
+// CFINITEONLY-NEXT: store <2 x float> [[TMP4]], ptr [[RETVAL]], align 8
+// CFINITEONLY-NEXT: [[TMP5:%.*]] = load double, ptr [[RETVAL]], align 8
+// CFINITEONLY-NEXT: ret double [[TMP5]]
//
// CLFINITEONLY: Function Attrs: convergent norecurse nounwind
// CLFINITEONLY-LABEL: define dso_local nofpclass(nan inf) double @call_extern_func_vec
-// CLFINITEONLY-SAME: (double noundef nofpclass(nan inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[B:%.*]], i32 noundef [[C_COERCE:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] {
+// CLFINITEONLY-SAME: (double noundef nofpclass(nan inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[B:%.*]], <2 x half> noundef nofpclass(nan inf) [[C:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] {
// CLFINITEONLY-NEXT: entry:
-// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) double @extern_func_vec(double noundef nofpclass(nan inf) [[A_COERCE]], <2 x double> noundef nofpclass(nan inf) [[B]], i32 noundef [[C_COERCE]]) #[[ATTR11]]
+// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) double @extern_func_vec(double noundef nofpclass(nan inf) [[A_COERCE]], <2 x double> noundef nofpclass(nan inf) [[B]], <2 x half> noundef nofpclass(nan inf) [[C]]) #[[ATTR11]]
// CLFINITEONLY-NEXT: ret double [[CALL]]
//
// NONANS: Function Attrs: noinline nounwind optnone
// NONANS-LABEL: define dso_local nofpclass(nan) double @call_extern_func_vec
-// NONANS-SAME: (double noundef nofpclass(nan) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[B:%.*]], i32 noundef [[C_COERCE:%.*]]) #[[ATTR2]] {
+// NONANS-SAME: (double noundef nofpclass(nan) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[B:%.*]], <2 x half> noundef nofpclass(nan) [[C:%.*]]) #[[ATTR2]] {
// NONANS-NEXT: entry:
// NONANS-NEXT: [[RETVAL:%.*]] = alloca <2 x float>, align 8
// NONANS-NEXT: [[A:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[C:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[A_ADDR:%.*]] = alloca <2 x float>, align 8
// NONANS-NEXT: [[B_ADDR:%.*]] = alloca <2 x double>, align 16
// NONANS-NEXT: [[C_ADDR:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NONANS-NEXT: [[COERCE4:%.*]] = alloca <2 x float>, align 8
+// NONANS-NEXT: [[COERCE2:%.*]] = alloca <2 x float>, align 8
// NONANS-NEXT: store double [[A_COERCE]], ptr [[A]], align 8
// NONANS-NEXT: [[A1:%.*]] = load <2 x float>, ptr [[A]], align 8
-// NONANS-NEXT: store i32 [[C_COERCE]], ptr [[C]], align 4
-// NONANS-NEXT: [[C2:%.*]] = load <2 x half>, ptr [[C]], align 4
// NONANS-NEXT: store <2 x float> [[A1]], ptr [[A_ADDR]], align 8
// NONANS-NEXT: store <2 x double> [[B]], ptr [[B_ADDR]], align 16
-// NONANS-NEXT: store <2 x half> [[C2]], ptr [[C_ADDR]], align 4
+// NONANS-NEXT: store <2 x half> [[C]], ptr [[C_ADDR]], align 4
// NONANS-NEXT: [[TMP0:%.*]] = load <2 x float>, ptr [[A_ADDR]], align 8
// NONANS-NEXT: [[TMP1:%.*]] = load <2 x double>, ptr [[B_ADDR]], align 16
// NONANS-NEXT: [[TMP2:%.*]] = load <2 x half>, ptr [[C_ADDR]], align 4
// NONANS-NEXT: store <2 x float> [[TMP0]], ptr [[COERCE]], align 8
// NONANS-NEXT: [[TMP3:%.*]] = load double, ptr [[COERCE]], align 8
-// NONANS-NEXT: store <2 x half> [[TMP2]], ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[TMP4:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) double @extern_func_vec(double noundef nofpclass(nan) [[TMP3]], <2 x double> noundef nofpclass(nan) [[TMP1]], i32 noundef [[TMP4]])
-// NONANS-NEXT: store double [[CALL]], ptr [[COERCE4]], align 8
-// NONANS-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[COERCE4]], align 8
-// NONANS-NEXT: store <2 x float> [[TMP5]], ptr [[RETVAL]], align 8
-// NONANS-NEXT: [[TMP6:%.*]] = load double, ptr [[RETVAL]], align 8
-// NONANS-NEXT: ret double [[TMP6]]
+// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) double @extern_func_vec(double noundef nofpclass(nan) [[TMP3]], <2 x double> noundef nofpclass(nan) [[TMP1]], <2 x half> noundef nofpclass(nan) [[TMP2]])
+// NONANS-NEXT: store double [[CALL]], ptr [[COERCE2]], align 8
+// NONANS-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[COERCE2]], align 8
+// NONANS-NEXT: store <2 x float> [[TMP4]], ptr [[RETVAL]], align 8
+// NONANS-NEXT: [[TMP5:%.*]] = load double, ptr [[RETVAL]], align 8
+// NONANS-NEXT: ret double [[TMP5]]
//
// NOINFS: Function Attrs: noinline nounwind optnone
// NOINFS-LABEL: define dso_local nofpclass(inf) double @call_extern_func_vec
-// NOINFS-SAME: (double noundef nofpclass(inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[B:%.*]], i32 noundef [[C_COERCE:%.*]]) #[[ATTR2]] {
+// NOINFS-SAME: (double noundef nofpclass(inf) [[A_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[B:%.*]], <2 x half> noundef nofpclass(inf) [[C:%.*]]) #[[ATTR2]] {
// NOINFS-NEXT: entry:
// NOINFS-NEXT: [[RETVAL:%.*]] = alloca <2 x float>, align 8
// NOINFS-NEXT: [[A:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[C:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[A_ADDR:%.*]] = alloca <2 x float>, align 8
// NOINFS-NEXT: [[B_ADDR:%.*]] = alloca <2 x double>, align 16
// NOINFS-NEXT: [[C_ADDR:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NOINFS-NEXT: [[COERCE4:%.*]] = alloca <2 x float>, align 8
+// NOINFS-NEXT: [[COERCE2:%.*]] = alloca <2 x float>, align 8
// NOINFS-NEXT: store double [[A_COERCE]], ptr [[A]], align 8
// NOINFS-NEXT: [[A1:%.*]] = load <2 x float>, ptr [[A]], align 8
-// NOINFS-NEXT: store i32 [[C_COERCE]], ptr [[C]], align 4
-// NOINFS-NEXT: [[C2:%.*]] = load <2 x half>, ptr [[C]], align 4
// NOINFS-NEXT: store <2 x float> [[A1]], ptr [[A_ADDR]], align 8
// NOINFS-NEXT: store <2 x double> [[B]], ptr [[B_ADDR]], align 16
-// NOINFS-NEXT: store <2 x half> [[C2]], ptr [[C_ADDR]], align 4
+// NOINFS-NEXT: store <2 x half> [[C]], ptr [[C_ADDR]], align 4
// NOINFS-NEXT: [[TMP0:%.*]] = load <2 x float>, ptr [[A_ADDR]], align 8
// NOINFS-NEXT: [[TMP1:%.*]] = load <2 x double>, ptr [[B_ADDR]], align 16
// NOINFS-NEXT: [[TMP2:%.*]] = load <2 x half>, ptr [[C_ADDR]], align 4
// NOINFS-NEXT: store <2 x float> [[TMP0]], ptr [[COERCE]], align 8
// NOINFS-NEXT: [[TMP3:%.*]] = load double, ptr [[COERCE]], align 8
-// NOINFS-NEXT: store <2 x half> [[TMP2]], ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[TMP4:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) double @extern_func_vec(double noundef nofpclass(inf) [[TMP3]], <2 x double> noundef nofpclass(inf) [[TMP1]], i32 noundef [[TMP4]])
-// NOINFS-NEXT: store double [[CALL]], ptr [[COERCE4]], align 8
-// NOINFS-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[COERCE4]], align 8
-// NOINFS-NEXT: store <2 x float> [[TMP5]], ptr [[RETVAL]], align 8
-// NOINFS-NEXT: [[TMP6:%.*]] = load double, ptr [[RETVAL]], align 8
-// NOINFS-NEXT: ret double [[TMP6]]
+// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) double @extern_func_vec(double noundef nofpclass(inf) [[TMP3]], <2 x double> noundef nofpclass(inf) [[TMP1]], <2 x half> noundef nofpclass(inf) [[TMP2]])
+// NOINFS-NEXT: store double [[CALL]], ptr [[COERCE2]], align 8
+// NOINFS-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[COERCE2]], align 8
+// NOINFS-NEXT: store <2 x float> [[TMP4]], ptr [[RETVAL]], align 8
+// NOINFS-NEXT: [[TMP5:%.*]] = load double, ptr [[RETVAL]], align 8
+// NOINFS-NEXT: ret double [[TMP5]]
//
float2 call_extern_func_vec(float2 a, double2 b, half2 c) {
return extern_func_vec(a, b, c);
@@ -845,12 +827,11 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// CFINITEONLY: Function Attrs: noinline nounwind optnone
// CFINITEONLY-LABEL: define dso_local nofpclass(nan inf) float @call_variadic
-// CFINITEONLY-SAME: (float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// CFINITEONLY-SAME: (float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(nan inf) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// CFINITEONLY-NEXT: entry:
// CFINITEONLY-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// CFINITEONLY-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// CFINITEONLY-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// CFINITEONLY-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// CFINITEONLY-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
// CFINITEONLY-NEXT: [[F16_ADDR:%.*]] = alloca half, align 2
@@ -858,33 +839,27 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// CFINITEONLY-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// CFINITEONLY-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// CFINITEONLY-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// CFINITEONLY-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// CFINITEONLY-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// CFINITEONLY-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// CFINITEONLY-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// CFINITEONLY-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// CFINITEONLY-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// CFINITEONLY-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// CFINITEONLY-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// CFINITEONLY-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// CFINITEONLY-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// CFINITEONLY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// CFINITEONLY-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// CFINITEONLY-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// CFINITEONLY-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// CFINITEONLY-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// CFINITEONLY-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// CFINITEONLY-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// CFINITEONLY-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// CFINITEONLY-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// CFINITEONLY-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// CFINITEONLY-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// CFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[TMP3]] to double
-// CFINITEONLY-NEXT: [[TMP4:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// CFINITEONLY-NEXT: [[TMP5:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// CFINITEONLY-NEXT: [[TMP6:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// CFINITEONLY-NEXT: [[TMP7:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// CFINITEONLY-NEXT: [[TMP8:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// CFINITEONLY-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// CFINITEONLY-NEXT: [[TMP0:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// CFINITEONLY-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// CFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[TMP1]] to double
+// CFINITEONLY-NEXT: [[TMP2:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// CFINITEONLY-NEXT: [[TMP3:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// CFINITEONLY-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// CFINITEONLY-NEXT: [[TMP5:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// CFINITEONLY-NEXT: [[TMP6:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// CFINITEONLY-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// CFINITEONLY-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// CFINITEONLY-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -894,57 +869,59 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// CFINITEONLY-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// CFINITEONLY-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// CFINITEONLY-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// CFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// CFINITEONLY-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// CFINITEONLY-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// CFINITEONLY-NEXT: store <2 x float> [[TMP6]], ptr [[COERCE]], align 8
-// CFINITEONLY-NEXT: [[TMP9:%.*]] = load double, ptr [[COERCE]], align 8
-// CFINITEONLY-NEXT: store <2 x half> [[TMP8]], ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[TMP10:%.*]] = load i32, ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// CFINITEONLY-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// CFINITEONLY-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// CFINITEONLY-NEXT: [[TMP11:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// CFINITEONLY-NEXT: store <2 x float> [[TMP4]], ptr [[COERCE]], align 8
+// CFINITEONLY-NEXT: [[TMP7:%.*]] = load double, ptr [[COERCE]], align 8
+// CFINITEONLY-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// CFINITEONLY-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// CFINITEONLY-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// CFINITEONLY-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// CFINITEONLY-NEXT: [[TMP8:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// CFINITEONLY-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// CFINITEONLY-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// CFINITEONLY-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// CFINITEONLY-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// CFINITEONLY-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// CFINITEONLY-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// CFINITEONLY-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// CFINITEONLY-NEXT: [[TMP12:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) float (float, ...) @variadic(float noundef nofpclass(nan inf) [[TMP2]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[TMP4]], half noundef nofpclass(nan inf) [[TMP5]], double noundef nofpclass(nan inf) [[TMP9]], <2 x double> noundef nofpclass(nan inf) [[TMP7]], i32 noundef [[TMP10]], <2 x float> noundef nofpclass(nan inf) [[TMP11]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan inf) [[TMP12]])
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// CFINITEONLY-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// CFINITEONLY-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) float (float, ...) @variadic(float noundef nofpclass(nan inf) [[TMP0]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[TMP2]], half noundef nofpclass(nan inf) [[TMP3]], double noundef nofpclass(nan inf) [[TMP7]], <2 x double> noundef nofpclass(nan inf) [[TMP5]], <2 x half> noundef nofpclass(nan inf) [[TMP6]], <2 x float> noundef nofpclass(nan inf) [[TMP8]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// CFINITEONLY-NEXT: ret float [[CALL]]
//
// CLFINITEONLY: Function Attrs: convergent norecurse nounwind
// CLFINITEONLY-LABEL: define dso_local nofpclass(nan inf) float @call_variadic
-// CLFINITEONLY-SAME: (float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE1:%.*]], ptr nofree noundef readonly byval({ half, half }) align 8 captures(none) [[CF16:%.*]]) local_unnamed_addr #[[ATTR5]] {
+// CLFINITEONLY-SAME: (float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], ptr nofree noundef readonly byval({ double, double }) align 8 captures(none) [[CF64:%.*]], <2 x half> noundef nofpclass(nan inf) [[CF16_COERCE:%.*]]) local_unnamed_addr #[[ATTR5]] {
// CLFINITEONLY-NEXT: entry:
// CLFINITEONLY-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
+// CLFINITEONLY-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// CLFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[F32]] to double
-// CLFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16]], align 8
-// CLFINITEONLY-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[CF16]], i64 2
-// CLFINITEONLY-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
+// CLFINITEONLY-NEXT: [[CF64_REAL:%.*]] = load double, ptr [[CF64]], align 8
+// CLFINITEONLY-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[CF64]], i64 8
+// CLFINITEONLY-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
+// CLFINITEONLY-NEXT: [[CF16_SROA_0_0_VEC_EXTRACT:%.*]] = extractelement <2 x half> [[CF16_COERCE]], i64 0
+// CLFINITEONLY-NEXT: [[CF16_SROA_0_2_VEC_EXTRACT:%.*]] = extractelement <2 x half> [[CF16_COERCE]], i64 1
// CLFINITEONLY-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[BYVAL_TEMP]]) #[[ATTR12:[0-9]+]]
// CLFINITEONLY-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8
-// CLFINITEONLY-NEXT: store double [[CF64_COERCE0]], ptr [[BYVAL_TEMP]], align 8
-// CLFINITEONLY-NEXT: store double [[CF64_COERCE1]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// CLFINITEONLY-NEXT: [[COERCE5_SROA_0_0_VEC_INSERT:%.*]] = insertelement <2 x half> poison, half [[CF16_REAL]], i64 0
-// CLFINITEONLY-NEXT: [[COERCE5_SROA_0_2_VEC_INSERT:%.*]] = insertelement <2 x half> [[COERCE5_SROA_0_0_VEC_INSERT]], half [[CF16_IMAG]], i64 1
-// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) float (float, ...) @variadic(float noundef nofpclass(nan inf) [[F32]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[F64]], half noundef nofpclass(nan inf) [[F16]], double noundef nofpclass(nan inf) [[V2F32_COERCE]], <2 x double> noundef nofpclass(nan inf) [[V2F64]], i32 noundef [[V2F16_COERCE]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE]], ptr noundef nonnull byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan inf) [[COERCE5_SROA_0_2_VEC_INSERT]]) #[[ATTR11]]
+// CLFINITEONLY-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP]], align 8
+// CLFINITEONLY-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
+// CLFINITEONLY-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[BYVAL_TEMP3]]) #[[ATTR12]]
+// CLFINITEONLY-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP3]], i64 2
+// CLFINITEONLY-NEXT: store half [[CF16_SROA_0_0_VEC_EXTRACT]], ptr [[BYVAL_TEMP3]], align 8
+// CLFINITEONLY-NEXT: store half [[CF16_SROA_0_2_VEC_EXTRACT]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) float (float, ...) @variadic(float noundef nofpclass(nan inf) [[F32]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[F64]], half noundef nofpclass(nan inf) [[F16]], double noundef nofpclass(nan inf) [[V2F32_COERCE]], <2 x double> noundef nofpclass(nan inf) [[V2F64]], <2 x half> noundef nofpclass(nan inf) [[V2F16]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE]], ptr noundef nonnull byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef nonnull byval({ half, half }) align 8 [[BYVAL_TEMP3]]) #[[ATTR11]]
// CLFINITEONLY-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[BYVAL_TEMP]]) #[[ATTR12]]
+// CLFINITEONLY-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[BYVAL_TEMP3]]) #[[ATTR12]]
// CLFINITEONLY-NEXT: ret float [[CALL]]
//
// NONANS: Function Attrs: noinline nounwind optnone
// NONANS-LABEL: define dso_local nofpclass(nan) float @call_variadic
-// NONANS-SAME: (float noundef nofpclass(nan) [[F32:%.*]], double noundef nofpclass(nan) [[F64:%.*]], half noundef nofpclass(nan) [[F16:%.*]], double noundef nofpclass(nan) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan) [[CF32_COERCE:%.*]], double noundef nofpclass(nan) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// NONANS-SAME: (float noundef nofpclass(nan) [[F32:%.*]], double noundef nofpclass(nan) [[F64:%.*]], half noundef nofpclass(nan) [[F16:%.*]], double noundef nofpclass(nan) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(nan) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// NONANS-NEXT: entry:
// NONANS-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// NONANS-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// NONANS-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// NONANS-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// NONANS-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
// NONANS-NEXT: [[F16_ADDR:%.*]] = alloca half, align 2
@@ -952,33 +929,27 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// NONANS-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// NONANS-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NONANS-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// NONANS-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// NONANS-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// NONANS-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// NONANS-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// NONANS-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// NONANS-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// NONANS-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// NONANS-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// NONANS-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// NONANS-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// NONANS-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// NONANS-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// NONANS-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// NONANS-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// NONANS-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// NONANS-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// NONANS-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// NONANS-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// NONANS-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// NONANS-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// NONANS-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NONANS-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NONANS-NEXT: [[CONV:%.*]] = fpext nnan float [[TMP3]] to double
-// NONANS-NEXT: [[TMP4:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// NONANS-NEXT: [[TMP5:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// NONANS-NEXT: [[TMP6:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// NONANS-NEXT: [[TMP7:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// NONANS-NEXT: [[TMP8:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// NONANS-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// NONANS-NEXT: [[TMP0:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NONANS-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NONANS-NEXT: [[CONV:%.*]] = fpext nnan float [[TMP1]] to double
+// NONANS-NEXT: [[TMP2:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// NONANS-NEXT: [[TMP3:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// NONANS-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// NONANS-NEXT: [[TMP5:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// NONANS-NEXT: [[TMP6:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// NONANS-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// NONANS-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// NONANS-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -988,38 +959,34 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// NONANS-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// NONANS-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// NONANS-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// NONANS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// NONANS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// NONANS-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// NONANS-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// NONANS-NEXT: store <2 x float> [[TMP6]], ptr [[COERCE]], align 8
-// NONANS-NEXT: [[TMP9:%.*]] = load double, ptr [[COERCE]], align 8
-// NONANS-NEXT: store <2 x half> [[TMP8]], ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[TMP10:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// NONANS-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// NONANS-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// NONANS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// NONANS-NEXT: [[TMP11:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// NONANS-NEXT: store <2 x float> [[TMP4]], ptr [[COERCE]], align 8
+// NONANS-NEXT: [[TMP7:%.*]] = load double, ptr [[COERCE]], align 8
+// NONANS-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// NONANS-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// NONANS-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// NONANS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// NONANS-NEXT: [[TMP8:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// NONANS-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// NONANS-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// NONANS-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// NONANS-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// NONANS-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// NONANS-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// NONANS-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// NONANS-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// NONANS-NEXT: [[TMP12:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) float (float, ...) @variadic(float noundef nofpclass(nan) [[TMP2]], double noundef nofpclass(nan) [[CONV]], double noundef nofpclass(nan) [[TMP4]], half noundef nofpclass(nan) [[TMP5]], double noundef nofpclass(nan) [[TMP9]], <2 x double> noundef nofpclass(nan) [[TMP7]], i32 noundef [[TMP10]], <2 x float> noundef nofpclass(nan) [[TMP11]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan) [[TMP12]])
+// NONANS-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// NONANS-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// NONANS-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// NONANS-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) float (float, ...) @variadic(float noundef nofpclass(nan) [[TMP0]], double noundef nofpclass(nan) [[CONV]], double noundef nofpclass(nan) [[TMP2]], half noundef nofpclass(nan) [[TMP3]], double noundef nofpclass(nan) [[TMP7]], <2 x double> noundef nofpclass(nan) [[TMP5]], <2 x half> noundef nofpclass(nan) [[TMP6]], <2 x float> noundef nofpclass(nan) [[TMP8]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// NONANS-NEXT: ret float [[CALL]]
//
// NOINFS: Function Attrs: noinline nounwind optnone
// NOINFS-LABEL: define dso_local nofpclass(inf) float @call_variadic
-// NOINFS-SAME: (float noundef nofpclass(inf) [[F32:%.*]], double noundef nofpclass(inf) [[F64:%.*]], half noundef nofpclass(inf) [[F16:%.*]], double noundef nofpclass(inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(inf) [[CF32_COERCE:%.*]], double noundef nofpclass(inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(inf) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// NOINFS-SAME: (float noundef nofpclass(inf) [[F32:%.*]], double noundef nofpclass(inf) [[F64:%.*]], half noundef nofpclass(inf) [[F16:%.*]], double noundef nofpclass(inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(inf) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(inf) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// NOINFS-NEXT: entry:
// NOINFS-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// NOINFS-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// NOINFS-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// NOINFS-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// NOINFS-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
// NOINFS-NEXT: [[F16_ADDR:%.*]] = alloca half, align 2
@@ -1027,33 +994,27 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// NOINFS-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// NOINFS-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NOINFS-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// NOINFS-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// NOINFS-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// NOINFS-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// NOINFS-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// NOINFS-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// NOINFS-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// NOINFS-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// NOINFS-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// NOINFS-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// NOINFS-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// NOINFS-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// NOINFS-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// NOINFS-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// NOINFS-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// NOINFS-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// NOINFS-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// NOINFS-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// NOINFS-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// NOINFS-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// NOINFS-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// NOINFS-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NOINFS-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NOINFS-NEXT: [[CONV:%.*]] = fpext ninf float [[TMP3]] to double
-// NOINFS-NEXT: [[TMP4:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// NOINFS-NEXT: [[TMP5:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// NOINFS-NEXT: [[TMP6:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// NOINFS-NEXT: [[TMP7:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// NOINFS-NEXT: [[TMP8:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// NOINFS-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// NOINFS-NEXT: [[TMP0:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NOINFS-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NOINFS-NEXT: [[CONV:%.*]] = fpext ninf float [[TMP1]] to double
+// NOINFS-NEXT: [[TMP2:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// NOINFS-NEXT: [[TMP3:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// NOINFS-NEXT: [[TMP4:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// NOINFS-NEXT: [[TMP5:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// NOINFS-NEXT: [[TMP6:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// NOINFS-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// NOINFS-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// NOINFS-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -1063,28 +1024,25 @@ _Complex _Float16 defined_complex_func_f16_ret(_Complex _Float16 c) {
// NOINFS-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// NOINFS-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// NOINFS-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// NOINFS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// NOINFS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// NOINFS-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// NOINFS-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// NOINFS-NEXT: store <2 x float> [[TMP6]], ptr [[COERCE]], align 8
-// NOINFS-NEXT: [[TMP9:%.*]] = load double, ptr [[COERCE]], align 8
-// NOINFS-NEXT: store <2 x half> [[TMP8]], ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[TMP10:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// NOINFS-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// NOINFS-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// NOINFS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// NOINFS-NEXT: [[TMP11:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// NOINFS-NEXT: store <2 x float> [[TMP4]], ptr [[COERCE]], align 8
+// NOINFS-NEXT: [[TMP7:%.*]] = load double, ptr [[COERCE]], align 8
+// NOINFS-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// NOINFS-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// NOINFS-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// NOINFS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// NOINFS-NEXT: [[TMP8:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// NOINFS-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// NOINFS-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// NOINFS-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// NOINFS-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// NOINFS-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// NOINFS-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// NOINFS-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// NOINFS-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// NOINFS-NEXT: [[TMP12:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) float (float, ...) @variadic(float noundef nofpclass(inf) [[TMP2]], double noundef nofpclass(inf) [[CONV]], double noundef nofpclass(inf) [[TMP4]], half noundef nofpclass(inf) [[TMP5]], double noundef nofpclass(inf) [[TMP9]], <2 x double> noundef nofpclass(inf) [[TMP7]], i32 noundef [[TMP10]], <2 x float> noundef nofpclass(inf) [[TMP11]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(inf) [[TMP12]])
+// NOINFS-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// NOINFS-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// NOINFS-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// NOINFS-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) float (float, ...) @variadic(float noundef nofpclass(inf) [[TMP0]], double noundef nofpclass(inf) [[CONV]], double noundef nofpclass(inf) [[TMP2]], half noundef nofpclass(inf) [[TMP3]], double noundef nofpclass(inf) [[TMP7]], <2 x double> noundef nofpclass(inf) [[TMP5]], <2 x half> noundef nofpclass(inf) [[TMP6]], <2 x float> noundef nofpclass(inf) [[TMP8]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// NOINFS-NEXT: ret float [[CALL]]
//
float call_variadic(float f32, double f64, _Float16 f16,
@@ -1095,12 +1053,11 @@ float call_variadic(float f32, double f64, _Float16 f16,
// CFINITEONLY: Function Attrs: noinline nounwind optnone
// CFINITEONLY-LABEL: define dso_local nofpclass(nan inf) float @call_variadic_indirect
-// CFINITEONLY-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// CFINITEONLY-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(nan inf) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// CFINITEONLY-NEXT: entry:
// CFINITEONLY-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// CFINITEONLY-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// CFINITEONLY-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// CFINITEONLY-NEXT: [[FPTR_ADDR:%.*]] = alloca ptr, align 8
// CFINITEONLY-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// CFINITEONLY-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
@@ -1109,35 +1066,29 @@ float call_variadic(float f32, double f64, _Float16 f16,
// CFINITEONLY-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// CFINITEONLY-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// CFINITEONLY-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// CFINITEONLY-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// CFINITEONLY-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// CFINITEONLY-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// CFINITEONLY-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// CFINITEONLY-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// CFINITEONLY-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// CFINITEONLY-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// CFINITEONLY-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// CFINITEONLY-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// CFINITEONLY-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// CFINITEONLY-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// CFINITEONLY-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// CFINITEONLY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// CFINITEONLY-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// CFINITEONLY-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// CFINITEONLY-NEXT: store ptr [[FPTR]], ptr [[FPTR_ADDR]], align 8
// CFINITEONLY-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// CFINITEONLY-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// CFINITEONLY-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// CFINITEONLY-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// CFINITEONLY-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// CFINITEONLY-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// CFINITEONLY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
-// CFINITEONLY-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// CFINITEONLY-NEXT: [[TMP4:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// CFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[TMP4]] to double
-// CFINITEONLY-NEXT: [[TMP5:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// CFINITEONLY-NEXT: [[TMP6:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// CFINITEONLY-NEXT: [[TMP7:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// CFINITEONLY-NEXT: [[TMP8:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// CFINITEONLY-NEXT: [[TMP9:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// CFINITEONLY-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// CFINITEONLY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
+// CFINITEONLY-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// CFINITEONLY-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// CFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[TMP2]] to double
+// CFINITEONLY-NEXT: [[TMP3:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// CFINITEONLY-NEXT: [[TMP4:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// CFINITEONLY-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// CFINITEONLY-NEXT: [[TMP6:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// CFINITEONLY-NEXT: [[TMP7:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// CFINITEONLY-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// CFINITEONLY-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// CFINITEONLY-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -1147,57 +1098,59 @@ float call_variadic(float f32, double f64, _Float16 f16,
// CFINITEONLY-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// CFINITEONLY-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// CFINITEONLY-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// CFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// CFINITEONLY-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// CFINITEONLY-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// CFINITEONLY-NEXT: store <2 x float> [[TMP7]], ptr [[COERCE]], align 8
-// CFINITEONLY-NEXT: [[TMP10:%.*]] = load double, ptr [[COERCE]], align 8
-// CFINITEONLY-NEXT: store <2 x half> [[TMP9]], ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[TMP11:%.*]] = load i32, ptr [[COERCE3]], align 4
-// CFINITEONLY-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// CFINITEONLY-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// CFINITEONLY-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// CFINITEONLY-NEXT: [[TMP12:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// CFINITEONLY-NEXT: store <2 x float> [[TMP5]], ptr [[COERCE]], align 8
+// CFINITEONLY-NEXT: [[TMP8:%.*]] = load double, ptr [[COERCE]], align 8
+// CFINITEONLY-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// CFINITEONLY-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// CFINITEONLY-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// CFINITEONLY-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// CFINITEONLY-NEXT: [[TMP9:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// CFINITEONLY-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// CFINITEONLY-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// CFINITEONLY-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// CFINITEONLY-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// CFINITEONLY-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// CFINITEONLY-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// CFINITEONLY-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// CFINITEONLY-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// CFINITEONLY-NEXT: [[TMP13:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) float (float, ...) [[TMP2]](float noundef nofpclass(nan inf) [[TMP3]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[TMP5]], half noundef nofpclass(nan inf) [[TMP6]], double noundef nofpclass(nan inf) [[TMP10]], <2 x double> noundef nofpclass(nan inf) [[TMP8]], i32 noundef [[TMP11]], <2 x float> noundef nofpclass(nan inf) [[TMP12]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan inf) [[TMP13]])
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// CFINITEONLY-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// CFINITEONLY-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// CFINITEONLY-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// CFINITEONLY-NEXT: [[CALL:%.*]] = call nnan ninf nofpclass(nan inf) float (float, ...) [[TMP0]](float noundef nofpclass(nan inf) [[TMP1]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[TMP3]], half noundef nofpclass(nan inf) [[TMP4]], double noundef nofpclass(nan inf) [[TMP8]], <2 x double> noundef nofpclass(nan inf) [[TMP6]], <2 x half> noundef nofpclass(nan inf) [[TMP7]], <2 x float> noundef nofpclass(nan inf) [[TMP9]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// CFINITEONLY-NEXT: ret float [[CALL]]
//
// CLFINITEONLY: Function Attrs: convergent norecurse nounwind
// CLFINITEONLY-LABEL: define dso_local nofpclass(nan inf) float @call_variadic_indirect
-// CLFINITEONLY-SAME: (ptr nofree noundef readonly captures(none) [[FPTR:%.*]], float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan inf) [[CF64_COERCE1:%.*]], ptr nofree noundef readonly byval({ half, half }) align 8 captures(none) [[CF16:%.*]]) local_unnamed_addr #[[ATTR5]] {
+// CLFINITEONLY-SAME: (ptr nofree noundef readonly captures(none) [[FPTR:%.*]], float noundef nofpclass(nan inf) [[F32:%.*]], double noundef nofpclass(nan inf) [[F64:%.*]], half noundef nofpclass(nan inf) [[F16:%.*]], double noundef nofpclass(nan inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE:%.*]], ptr nofree noundef readonly byval({ double, double }) align 8 captures(none) [[CF64:%.*]], <2 x half> noundef nofpclass(nan inf) [[CF16_COERCE:%.*]]) local_unnamed_addr #[[ATTR5]] {
// CLFINITEONLY-NEXT: entry:
// CLFINITEONLY-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
+// CLFINITEONLY-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// CLFINITEONLY-NEXT: [[CONV:%.*]] = fpext nnan ninf float [[F32]] to double
-// CLFINITEONLY-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16]], align 8
-// CLFINITEONLY-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[CF16]], i64 2
-// CLFINITEONLY-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
+// CLFINITEONLY-NEXT: [[CF64_REAL:%.*]] = load double, ptr [[CF64]], align 8
+// CLFINITEONLY-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[CF64]], i64 8
+// CLFINITEONLY-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
+// CLFINITEONLY-NEXT: [[CF16_SROA_0_0_VEC_EXTRACT:%.*]] = extractelement <2 x half> [[CF16_COERCE]], i64 0
+// CLFINITEONLY-NEXT: [[CF16_SROA_0_2_VEC_EXTRACT:%.*]] = extractelement <2 x half> [[CF16_COERCE]], i64 1
// CLFINITEONLY-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[BYVAL_TEMP]]) #[[ATTR12]]
// CLFINITEONLY-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8
-// CLFINITEONLY-NEXT: store double [[CF64_COERCE0]], ptr [[BYVAL_TEMP]], align 8
-// CLFINITEONLY-NEXT: store double [[CF64_COERCE1]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// CLFINITEONLY-NEXT: [[COERCE5_SROA_0_0_VEC_INSERT:%.*]] = insertelement <2 x half> poison, half [[CF16_REAL]], i64 0
-// CLFINITEONLY-NEXT: [[COERCE5_SROA_0_2_VEC_INSERT:%.*]] = insertelement <2 x half> [[COERCE5_SROA_0_0_VEC_INSERT]], half [[CF16_IMAG]], i64 1
-// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) float (float, ...) [[FPTR]](float noundef nofpclass(nan inf) [[F32]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[F64]], half noundef nofpclass(nan inf) [[F16]], double noundef nofpclass(nan inf) [[V2F32_COERCE]], <2 x double> noundef nofpclass(nan inf) [[V2F64]], i32 noundef [[V2F16_COERCE]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE]], ptr noundef nonnull byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan inf) [[COERCE5_SROA_0_2_VEC_INSERT]]) #[[ATTR11]]
+// CLFINITEONLY-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP]], align 8
+// CLFINITEONLY-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
+// CLFINITEONLY-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[BYVAL_TEMP3]]) #[[ATTR12]]
+// CLFINITEONLY-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP3]], i64 2
+// CLFINITEONLY-NEXT: store half [[CF16_SROA_0_0_VEC_EXTRACT]], ptr [[BYVAL_TEMP3]], align 8
+// CLFINITEONLY-NEXT: store half [[CF16_SROA_0_2_VEC_EXTRACT]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// CLFINITEONLY-NEXT: [[CALL:%.*]] = tail call nnan ninf nofpclass(nan inf) float (float, ...) [[FPTR]](float noundef nofpclass(nan inf) [[F32]], double noundef nofpclass(nan inf) [[CONV]], double noundef nofpclass(nan inf) [[F64]], half noundef nofpclass(nan inf) [[F16]], double noundef nofpclass(nan inf) [[V2F32_COERCE]], <2 x double> noundef nofpclass(nan inf) [[V2F64]], <2 x half> noundef nofpclass(nan inf) [[V2F16]], <2 x float> noundef nofpclass(nan inf) [[CF32_COERCE]], ptr noundef nonnull byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef nonnull byval({ half, half }) align 8 [[BYVAL_TEMP3]]) #[[ATTR11]]
// CLFINITEONLY-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[BYVAL_TEMP]]) #[[ATTR12]]
+// CLFINITEONLY-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[BYVAL_TEMP3]]) #[[ATTR12]]
// CLFINITEONLY-NEXT: ret float [[CALL]]
//
// NONANS: Function Attrs: noinline nounwind optnone
// NONANS-LABEL: define dso_local nofpclass(nan) float @call_variadic_indirect
-// NONANS-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(nan) [[F32:%.*]], double noundef nofpclass(nan) [[F64:%.*]], half noundef nofpclass(nan) [[F16:%.*]], double noundef nofpclass(nan) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(nan) [[CF32_COERCE:%.*]], double noundef nofpclass(nan) [[CF64_COERCE0:%.*]], double noundef nofpclass(nan) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// NONANS-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(nan) [[F32:%.*]], double noundef nofpclass(nan) [[F64:%.*]], half noundef nofpclass(nan) [[F16:%.*]], double noundef nofpclass(nan) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(nan) [[V2F64:%.*]], <2 x half> noundef nofpclass(nan) [[V2F16:%.*]], <2 x float> noundef nofpclass(nan) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(nan) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// NONANS-NEXT: entry:
// NONANS-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// NONANS-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// NONANS-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// NONANS-NEXT: [[FPTR_ADDR:%.*]] = alloca ptr, align 8
// NONANS-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// NONANS-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
@@ -1206,35 +1159,29 @@ float call_variadic(float f32, double f64, _Float16 f16,
// NONANS-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// NONANS-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// NONANS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NONANS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NONANS-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// NONANS-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// NONANS-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// NONANS-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// NONANS-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// NONANS-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// NONANS-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// NONANS-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// NONANS-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// NONANS-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// NONANS-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// NONANS-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// NONANS-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// NONANS-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// NONANS-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// NONANS-NEXT: store ptr [[FPTR]], ptr [[FPTR_ADDR]], align 8
// NONANS-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// NONANS-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// NONANS-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// NONANS-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// NONANS-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// NONANS-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// NONANS-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
-// NONANS-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NONANS-NEXT: [[TMP4:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NONANS-NEXT: [[CONV:%.*]] = fpext nnan float [[TMP4]] to double
-// NONANS-NEXT: [[TMP5:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// NONANS-NEXT: [[TMP6:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// NONANS-NEXT: [[TMP7:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// NONANS-NEXT: [[TMP8:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// NONANS-NEXT: [[TMP9:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// NONANS-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// NONANS-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
+// NONANS-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NONANS-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NONANS-NEXT: [[CONV:%.*]] = fpext nnan float [[TMP2]] to double
+// NONANS-NEXT: [[TMP3:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// NONANS-NEXT: [[TMP4:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// NONANS-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// NONANS-NEXT: [[TMP6:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// NONANS-NEXT: [[TMP7:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// NONANS-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// NONANS-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// NONANS-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -1244,38 +1191,34 @@ float call_variadic(float f32, double f64, _Float16 f16,
// NONANS-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// NONANS-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// NONANS-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// NONANS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// NONANS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// NONANS-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// NONANS-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// NONANS-NEXT: store <2 x float> [[TMP7]], ptr [[COERCE]], align 8
-// NONANS-NEXT: [[TMP10:%.*]] = load double, ptr [[COERCE]], align 8
-// NONANS-NEXT: store <2 x half> [[TMP9]], ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[TMP11:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NONANS-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// NONANS-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// NONANS-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// NONANS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// NONANS-NEXT: [[TMP12:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// NONANS-NEXT: store <2 x float> [[TMP5]], ptr [[COERCE]], align 8
+// NONANS-NEXT: [[TMP8:%.*]] = load double, ptr [[COERCE]], align 8
+// NONANS-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// NONANS-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// NONANS-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// NONANS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// NONANS-NEXT: [[TMP9:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// NONANS-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// NONANS-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// NONANS-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// NONANS-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// NONANS-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// NONANS-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// NONANS-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// NONANS-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// NONANS-NEXT: [[TMP13:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) float (float, ...) [[TMP2]](float noundef nofpclass(nan) [[TMP3]], double noundef nofpclass(nan) [[CONV]], double noundef nofpclass(nan) [[TMP5]], half noundef nofpclass(nan) [[TMP6]], double noundef nofpclass(nan) [[TMP10]], <2 x double> noundef nofpclass(nan) [[TMP8]], i32 noundef [[TMP11]], <2 x float> noundef nofpclass(nan) [[TMP12]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(nan) [[TMP13]])
+// NONANS-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// NONANS-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// NONANS-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// NONANS-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// NONANS-NEXT: [[CALL:%.*]] = call nnan nofpclass(nan) float (float, ...) [[TMP0]](float noundef nofpclass(nan) [[TMP1]], double noundef nofpclass(nan) [[CONV]], double noundef nofpclass(nan) [[TMP3]], half noundef nofpclass(nan) [[TMP4]], double noundef nofpclass(nan) [[TMP8]], <2 x double> noundef nofpclass(nan) [[TMP6]], <2 x half> noundef nofpclass(nan) [[TMP7]], <2 x float> noundef nofpclass(nan) [[TMP9]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// NONANS-NEXT: ret float [[CALL]]
//
// NOINFS: Function Attrs: noinline nounwind optnone
// NOINFS-LABEL: define dso_local nofpclass(inf) float @call_variadic_indirect
-// NOINFS-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(inf) [[F32:%.*]], double noundef nofpclass(inf) [[F64:%.*]], half noundef nofpclass(inf) [[F16:%.*]], double noundef nofpclass(inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[V2F64:%.*]], i32 noundef [[V2F16_COERCE:%.*]], <2 x float> noundef nofpclass(inf) [[CF32_COERCE:%.*]], double noundef nofpclass(inf) [[CF64_COERCE0:%.*]], double noundef nofpclass(inf) [[CF64_COERCE1:%.*]], ptr noundef byval({ half, half }) align 8 [[CF16:%.*]]) #[[ATTR2]] {
+// NOINFS-SAME: (ptr noundef [[FPTR:%.*]], float noundef nofpclass(inf) [[F32:%.*]], double noundef nofpclass(inf) [[F64:%.*]], half noundef nofpclass(inf) [[F16:%.*]], double noundef nofpclass(inf) [[V2F32_COERCE:%.*]], <2 x double> noundef nofpclass(inf) [[V2F64:%.*]], <2 x half> noundef nofpclass(inf) [[V2F16:%.*]], <2 x float> noundef nofpclass(inf) [[CF32_COERCE:%.*]], ptr noundef byval({ double, double }) align 8 [[CF64:%.*]], <2 x half> noundef nofpclass(inf) [[CF16_COERCE:%.*]]) #[[ATTR2]] {
// NOINFS-NEXT: entry:
// NOINFS-NEXT: [[V2F32:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[V2F16:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[CF32:%.*]] = alloca { float, float }, align 4
-// NOINFS-NEXT: [[CF64:%.*]] = alloca { double, double }, align 8
+// NOINFS-NEXT: [[CF16:%.*]] = alloca { half, half }, align 2
// NOINFS-NEXT: [[FPTR_ADDR:%.*]] = alloca ptr, align 8
// NOINFS-NEXT: [[F32_ADDR:%.*]] = alloca float, align 4
// NOINFS-NEXT: [[F64_ADDR:%.*]] = alloca double, align 8
@@ -1284,35 +1227,29 @@ float call_variadic(float f32, double f64, _Float16 f16,
// NOINFS-NEXT: [[V2F64_ADDR:%.*]] = alloca <2 x double>, align 16
// NOINFS-NEXT: [[V2F16_ADDR:%.*]] = alloca <2 x half>, align 4
// NOINFS-NEXT: [[COERCE:%.*]] = alloca <2 x float>, align 8
-// NOINFS-NEXT: [[COERCE3:%.*]] = alloca <2 x half>, align 4
-// NOINFS-NEXT: [[COERCE4:%.*]] = alloca { float, float }, align 4
+// NOINFS-NEXT: [[COERCE2:%.*]] = alloca { float, float }, align 4
// NOINFS-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8
-// NOINFS-NEXT: [[COERCE5:%.*]] = alloca { half, half }, align 2
+// NOINFS-NEXT: [[BYVAL_TEMP3:%.*]] = alloca { half, half }, align 8
// NOINFS-NEXT: store double [[V2F32_COERCE]], ptr [[V2F32]], align 8
// NOINFS-NEXT: [[V2F321:%.*]] = load <2 x float>, ptr [[V2F32]], align 8
-// NOINFS-NEXT: store i32 [[V2F16_COERCE]], ptr [[V2F16]], align 4
-// NOINFS-NEXT: [[V2F162:%.*]] = load <2 x half>, ptr [[V2F16]], align 4
// NOINFS-NEXT: store <2 x float> [[CF32_COERCE]], ptr [[CF32]], align 4
-// NOINFS-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 0
-// NOINFS-NEXT: store double [[CF64_COERCE0]], ptr [[TMP0]], align 8
-// NOINFS-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
-// NOINFS-NEXT: store double [[CF64_COERCE1]], ptr [[TMP1]], align 8
+// NOINFS-NEXT: store <2 x half> [[CF16_COERCE]], ptr [[CF16]], align 2
// NOINFS-NEXT: store ptr [[FPTR]], ptr [[FPTR_ADDR]], align 8
// NOINFS-NEXT: store float [[F32]], ptr [[F32_ADDR]], align 4
// NOINFS-NEXT: store double [[F64]], ptr [[F64_ADDR]], align 8
// NOINFS-NEXT: store half [[F16]], ptr [[F16_ADDR]], align 2
// NOINFS-NEXT: store <2 x float> [[V2F321]], ptr [[V2F32_ADDR]], align 8
// NOINFS-NEXT: store <2 x double> [[V2F64]], ptr [[V2F64_ADDR]], align 16
-// NOINFS-NEXT: store <2 x half> [[V2F162]], ptr [[V2F16_ADDR]], align 4
-// NOINFS-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
-// NOINFS-NEXT: [[TMP3:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NOINFS-NEXT: [[TMP4:%.*]] = load float, ptr [[F32_ADDR]], align 4
-// NOINFS-NEXT: [[CONV:%.*]] = fpext ninf float [[TMP4]] to double
-// NOINFS-NEXT: [[TMP5:%.*]] = load double, ptr [[F64_ADDR]], align 8
-// NOINFS-NEXT: [[TMP6:%.*]] = load half, ptr [[F16_ADDR]], align 2
-// NOINFS-NEXT: [[TMP7:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
-// NOINFS-NEXT: [[TMP8:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
-// NOINFS-NEXT: [[TMP9:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
+// NOINFS-NEXT: store <2 x half> [[V2F16]], ptr [[V2F16_ADDR]], align 4
+// NOINFS-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FPTR_ADDR]], align 8
+// NOINFS-NEXT: [[TMP1:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NOINFS-NEXT: [[TMP2:%.*]] = load float, ptr [[F32_ADDR]], align 4
+// NOINFS-NEXT: [[CONV:%.*]] = fpext ninf float [[TMP2]] to double
+// NOINFS-NEXT: [[TMP3:%.*]] = load double, ptr [[F64_ADDR]], align 8
+// NOINFS-NEXT: [[TMP4:%.*]] = load half, ptr [[F16_ADDR]], align 2
+// NOINFS-NEXT: [[TMP5:%.*]] = load <2 x float>, ptr [[V2F32_ADDR]], align 8
+// NOINFS-NEXT: [[TMP6:%.*]] = load <2 x double>, ptr [[V2F64_ADDR]], align 16
+// NOINFS-NEXT: [[TMP7:%.*]] = load <2 x half>, ptr [[V2F16_ADDR]], align 4
// NOINFS-NEXT: [[CF32_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 0
// NOINFS-NEXT: [[CF32_REAL:%.*]] = load float, ptr [[CF32_REALP]], align 4
// NOINFS-NEXT: [[CF32_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[CF32]], i32 0, i32 1
@@ -1322,28 +1259,25 @@ float call_variadic(float f32, double f64, _Float16 f16,
// NOINFS-NEXT: [[CF64_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[CF64]], i32 0, i32 1
// NOINFS-NEXT: [[CF64_IMAG:%.*]] = load double, ptr [[CF64_IMAGP]], align 8
// NOINFS-NEXT: [[CF16_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 0
-// NOINFS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 8
+// NOINFS-NEXT: [[CF16_REAL:%.*]] = load half, ptr [[CF16_REALP]], align 2
// NOINFS-NEXT: [[CF16_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[CF16]], i32 0, i32 1
// NOINFS-NEXT: [[CF16_IMAG:%.*]] = load half, ptr [[CF16_IMAGP]], align 2
-// NOINFS-NEXT: store <2 x float> [[TMP7]], ptr [[COERCE]], align 8
-// NOINFS-NEXT: [[TMP10:%.*]] = load double, ptr [[COERCE]], align 8
-// NOINFS-NEXT: store <2 x half> [[TMP9]], ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[TMP11:%.*]] = load i32, ptr [[COERCE3]], align 4
-// NOINFS-NEXT: [[COERCE4_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 0
-// NOINFS-NEXT: [[COERCE4_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE4]], i32 0, i32 1
-// NOINFS-NEXT: store float [[CF32_REAL]], ptr [[COERCE4_REALP]], align 4
-// NOINFS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE4_IMAGP]], align 4
-// NOINFS-NEXT: [[TMP12:%.*]] = load <2 x float>, ptr [[COERCE4]], align 4
+// NOINFS-NEXT: store <2 x float> [[TMP5]], ptr [[COERCE]], align 8
+// NOINFS-NEXT: [[TMP8:%.*]] = load double, ptr [[COERCE]], align 8
+// NOINFS-NEXT: [[COERCE2_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 0
+// NOINFS-NEXT: [[COERCE2_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE2]], i32 0, i32 1
+// NOINFS-NEXT: store float [[CF32_REAL]], ptr [[COERCE2_REALP]], align 4
+// NOINFS-NEXT: store float [[CF32_IMAG]], ptr [[COERCE2_IMAGP]], align 4
+// NOINFS-NEXT: [[TMP9:%.*]] = load <2 x float>, ptr [[COERCE2]], align 4
// NOINFS-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0
// NOINFS-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1
// NOINFS-NEXT: store double [[CF64_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8
// NOINFS-NEXT: store double [[CF64_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8
-// NOINFS-NEXT: [[COERCE5_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 0
-// NOINFS-NEXT: [[COERCE5_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[COERCE5]], i32 0, i32 1
-// NOINFS-NEXT: store half [[CF16_REAL]], ptr [[COERCE5_REALP]], align 2
-// NOINFS-NEXT: store half [[CF16_IMAG]], ptr [[COERCE5_IMAGP]], align 2
-// NOINFS-NEXT: [[TMP13:%.*]] = load <2 x half>, ptr [[COERCE5]], align 2
-// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) float (float, ...) [[TMP2]](float noundef nofpclass(inf) [[TMP3]], double noundef nofpclass(inf) [[CONV]], double noundef nofpclass(inf) [[TMP5]], half noundef nofpclass(inf) [[TMP6]], double noundef nofpclass(inf) [[TMP10]], <2 x double> noundef nofpclass(inf) [[TMP8]], i32 noundef [[TMP11]], <2 x float> noundef nofpclass(inf) [[TMP12]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], <2 x half> noundef nofpclass(inf) [[TMP13]])
+// NOINFS-NEXT: [[BYVAL_TEMP3_REALP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 0
+// NOINFS-NEXT: [[BYVAL_TEMP3_IMAGP:%.*]] = getelementptr inbounds nuw { half, half }, ptr [[BYVAL_TEMP3]], i32 0, i32 1
+// NOINFS-NEXT: store half [[CF16_REAL]], ptr [[BYVAL_TEMP3_REALP]], align 8
+// NOINFS-NEXT: store half [[CF16_IMAG]], ptr [[BYVAL_TEMP3_IMAGP]], align 2
+// NOINFS-NEXT: [[CALL:%.*]] = call ninf nofpclass(inf) float (float, ...) [[TMP0]](float noundef nofpclass(inf) [[TMP1]], double noundef nofpclass(inf) [[CONV]], double noundef nofpclass(inf) [[TMP3]], half noundef nofpclass(inf) [[TMP4]], double noundef nofpclass(inf) [[TMP8]], <2 x double> noundef nofpclass(inf) [[TMP6]], <2 x half> noundef nofpclass(inf) [[TMP7]], <2 x float> noundef nofpclass(inf) [[TMP9]], ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]], ptr noundef byval({ half, half }) align 8 [[BYVAL_TEMP3]])
// NOINFS-NEXT: ret float [[CALL]]
//
float call_variadic_indirect(float fptr(float, ...), float f32, double f64, _Float16 f16,
diff --git a/clang/test/CodeGen/sysv_abi.c b/clang/test/CodeGen/sysv_abi.c
index 81d2b26a09629..ed7a1e0f5ab71 100644
--- a/clang/test/CodeGen/sysv_abi.c
+++ b/clang/test/CodeGen/sysv_abi.c
@@ -1,8 +1,10 @@
-// RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX
-// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX
-// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX
-// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX
-// RUN: %clang_cc1 -triple x86_64-uefi -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX512
+// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX512
+// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX512
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX512
+// RUN: %clang_cc1 -triple x86_64-uefi -emit-llvm -target-cpu skylake-avx512 < %s | FileCheck %s --check-prefixes=CHECK,AVX512
+// Note: x86-64-v3 implies support for AVX2 but not AVX512
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -target-cpu x86-64-v3 < %s | FileCheck %s --check-prefixes=CHECK,AVX2
// RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm < %s | FileCheck %s --check-prefixes=CHECK,NOAVX
// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm < %s | FileCheck %s --check-prefixes=CHECK,NOAVX
// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm < %s | FileCheck %s --check-prefixes=CHECK,NOAVX
@@ -45,13 +47,17 @@ void use_vectors(void) {
}
// CHECK: define {{(dso_local )?}}void @use_vectors()
-// AVX: call {{(x86_64_sysvcc )?}}<8 x float> @get_m256()
-// AVX: call {{(x86_64_sysvcc )?}}void @take_m256(<8 x float> noundef %{{.*}})
-// AVX: call {{(x86_64_sysvcc )?}}<16 x float> @get_m512()
-// AVX: call {{(x86_64_sysvcc )?}}void @take_m512(<16 x float> noundef %{{.*}})
-// NOAVX: call {{(x86_64_sysvcc )?}}<8 x float> @get_m256()
+// AVX512: call {{(x86_64_sysvcc )?}}<8 x float> @get_m256()
+// AVX512: call {{(x86_64_sysvcc )?}}void @take_m256(<8 x float> noundef %{{.*}})
+// AVX512: call {{(x86_64_sysvcc )?}}<16 x float> @get_m512()
+// AVX512: call {{(x86_64_sysvcc )?}}void @take_m512(<16 x float> noundef %{{.*}})
+// AVX2: call {{(x86_64_sysvcc )?}}<8 x float> @get_m256()
+// AVX2: call {{(x86_64_sysvcc )?}}void @take_m256(<8 x float> noundef %{{.*}})
+// AVX2: call {{(x86_64_sysvcc )?}}void @get_m512(ptr {{[^,]*}} sret(<16 x float>) align 64 %{{.*}})
+// AVX2: call {{(x86_64_sysvcc )?}}void @take_m512(ptr noundef byval(<16 x float>) align 64 %{{.*}})
+// NOAVX: call {{(x86_64_sysvcc )?}}void @get_m256(ptr {{[^,]*}} sret(<8 x float>) align 32 %{{.*}})
// NOAVX: call {{(x86_64_sysvcc )?}}void @take_m256(ptr noundef byval(<8 x float>) align 32 %{{.*}})
-// NOAVX: call {{(x86_64_sysvcc )?}}<16 x float> @get_m512()
+// NOAVX: call {{(x86_64_sysvcc )?}}void @get_m512(ptr {{[^,]*}} sret(<16 x float>) align 64 %{{.*}})
// NOAVX: call {{(x86_64_sysvcc )?}}void @take_m512(ptr noundef byval(<16 x float>) align 64 %{{.*}})
// Added test to explicitly cover the case when __attribute__((target("avx"))) is used
diff --git a/clang/test/CodeGenCXX/ext-int-vector-abi.cpp b/clang/test/CodeGenCXX/ext-int-vector-abi.cpp
index f9d864d6217cd..a5cffde03fc2f 100644
--- a/clang/test/CodeGenCXX/ext-int-vector-abi.cpp
+++ b/clang/test/CodeGenCXX/ext-int-vector-abi.cpp
@@ -61,7 +61,7 @@ uint32_t4s ManglingTestRetParam(uint32_t4s Param) {
typedef unsigned _BitInt(64) uint64_t4 __attribute__((ext_vector_type(4)));
uint64_t4 ManglingTestRetParam(uint64_t4 Param) {
-// LIN64: define{{.*}} <4 x i64> @_Z20ManglingTestRetParamDv4_DU64_(ptr byval(<4 x i64>) align 32 %
+// LIN64: define{{.*}} void @_Z20ManglingTestRetParamDv4_DU64_(ptr{{.*}}sret(<4 x i64>) align 32 %{{.*}}, ptr byval(<4 x i64>) align 32 %
// LIN32: define{{.*}} <4 x i64> @_Z20ManglingTestRetParamDv4_DU64_(<4 x i64> %
// WIN64: define dso_local <4 x i64> @"?ManglingTestRetParam@@YAT?$__vector at U?$_UBitInt@$0EA@@__clang@@$03 at __clang@@T12@@Z"(<4 x i64> %
// WIN32: define dso_local <4 x i64> @"?ManglingTestRetParam@@YAT?$__vector at U?$_UBitInt@$0EA@@__clang@@$03 at __clang@@T12@@Z"(<4 x i64> inreg %
@@ -70,7 +70,7 @@ uint64_t4 ManglingTestRetParam(uint64_t4 Param) {
typedef unsigned long long uint64_t4s __attribute__((ext_vector_type(4)));
uint64_t4s ManglingTestRetParam(uint64_t4s Param) {
-// LIN64: define{{.*}} <4 x i64> @_Z20ManglingTestRetParamDv4_y(ptr byval(<4 x i64>) align 32 %
+// LIN64: define{{.*}} void @_Z20ManglingTestRetParamDv4_y(ptr{{.*}}sret(<4 x i64>) align 32 %{{.*}}, ptr byval(<4 x i64>) align 32 %
// LIN32: define{{.*}} <4 x i64> @_Z20ManglingTestRetParamDv4_y(<4 x i64> %
// WIN64: define dso_local <4 x i64> @"?ManglingTestRetParam@@YAT?$__vector at _K$03 at __clang@@T12@@Z"(<4 x i64> %
// WIN32: define dso_local <4 x i64> @"?ManglingTestRetParam@@YAT?$__vector at _K$03 at __clang@@T12@@Z"(<4 x i64> inreg %
@@ -79,7 +79,7 @@ uint64_t4s ManglingTestRetParam(uint64_t4s Param) {
typedef _BitInt(32) vint32_t8 __attribute__((vector_size(32)));
vint32_t8 ManglingTestRetParam(vint32_t8 Param) {
-// LIN64: define{{.*}} <8 x i32> @_Z20ManglingTestRetParamDv8_DB32_(ptr byval(<8 x i32>) align 32 %
+// LIN64: define{{.*}} void @_Z20ManglingTestRetParamDv8_DB32_(ptr{{.*}}sret(<8 x i32>) align 32 %{{.*}}, ptr byval(<8 x i32>) align 32 %
// LIN32: define{{.*}} <8 x i32> @_Z20ManglingTestRetParamDv8_DB32_(<8 x i32> %
// WIN64: define dso_local <8 x i32> @"?ManglingTestRetParam@@YA?AT?$__vector at U?$_BitInt@$0CA@@__clang@@$07 at __clang@@T12@@Z"(<8 x i32> %
// WIN32: define dso_local <8 x i32> @"?ManglingTestRetParam@@YA?AT?$__vector at U?$_BitInt@$0CA@@__clang@@$07 at __clang@@T12@@Z"(<8 x i32> inreg %
@@ -88,7 +88,7 @@ vint32_t8 ManglingTestRetParam(vint32_t8 Param) {
typedef int vint32_t8i __attribute__((vector_size(32)));
vint32_t8i ManglingTestRetParam(vint32_t8i Param) {
-// LIN64: define{{.*}} <8 x i32> @_Z20ManglingTestRetParamDv8_i(ptr byval(<8 x i32>) align 32 %
+// LIN64: define{{.*}} void @_Z20ManglingTestRetParamDv8_i(ptr{{.*}}sret(<8 x i32>) align 32 %{{.*}}, ptr byval(<8 x i32>) align 32 %
// LIN32: define{{.*}} <8 x i32> @_Z20ManglingTestRetParamDv8_i(<8 x i32> %
// WIN64: define dso_local <8 x i32> @"?ManglingTestRetParam@@YA?AT?$__vector at H$07 at __clang@@T12@@Z"(<8 x i32> %
// WIN32: define dso_local <8 x i32> @"?ManglingTestRetParam@@YA?AT?$__vector at H$07 at __clang@@T12@@Z"(<8 x i32> inreg %
diff --git a/clang/test/CodeGenCXX/matrix-vector-bit-int.cpp b/clang/test/CodeGenCXX/matrix-vector-bit-int.cpp
index 4be1cb3067c2f..f5eaf7ba0207e 100644
--- a/clang/test/CodeGenCXX/matrix-vector-bit-int.cpp
+++ b/clang/test/CodeGenCXX/matrix-vector-bit-int.cpp
@@ -51,8 +51,8 @@ i32x3 v2(i32x3 a) {
return a + a;
}
-// CHECK-LABEL: define dso_local noundef <3 x i512> @_Z2v3Dv3_DB512_(
-// CHECK-SAME: ptr noundef byval(<3 x i512>) align 256 [[TMP0:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-LABEL: define dso_local void @_Z2v3Dv3_DB512_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(<3 x i512>) align 256 [[AGG_RESULT:%.*]], ptr noundef byval(<3 x i512>) align 256 [[TMP0:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <3 x i512>, align 256
// CHECK-NEXT: [[LOADVECN:%.*]] = load <4 x i512>, ptr [[TMP0]], align 256
@@ -64,7 +64,13 @@ i32x3 v2(i32x3 a) {
// CHECK-NEXT: [[LOADVECN3:%.*]] = load <4 x i512>, ptr [[A_ADDR]], align 256
// CHECK-NEXT: [[EXTRACTVEC4:%.*]] = shufflevector <4 x i512> [[LOADVECN3]], <4 x i512> poison, <3 x i32> <i32 0, i32 1, i32 2>
// CHECK-NEXT: [[ADD:%.*]] = add <3 x i512> [[EXTRACTVEC2]], [[EXTRACTVEC4]]
-// CHECK-NEXT: ret <3 x i512> [[ADD]]
+// CHECK-NEXT: [[EXTRACTVEC5:%.*]] = shufflevector <3 x i512> [[ADD]], <3 x i512> undef, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+// CHECK-NEXT: store <4 x i512> [[EXTRACTVEC5]], ptr [[AGG_RESULT]], align 256
+// CHECK-NEXT: [[LOADVECN6:%.*]] = load <4 x i512>, ptr [[AGG_RESULT]], align 256
+// CHECK-NEXT: [[EXTRACTVEC7:%.*]] = shufflevector <4 x i512> [[LOADVECN6]], <4 x i512> poison, <3 x i32> <i32 0, i32 1, i32 2>
+// CHECK-NEXT: [[EXTRACTVEC8:%.*]] = shufflevector <3 x i512> [[EXTRACTVEC7]], <3 x i512> undef, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+// CHECK-NEXT: store <4 x i512> [[EXTRACTVEC8]], ptr [[AGG_RESULT]], align 256
+// CHECK-NEXT: ret void
//
i512x3 v3(i512x3 a) {
return a + a;
@@ -97,7 +103,7 @@ i4x3 v4(i4x3 a) {
}
// CHECK-LABEL: define dso_local noundef <9 x i8> @_Z2m1u11matrix_typeILm3ELm3EDB8_E(
-// CHECK-SAME: <9 x i8> noundef [[A:%.*]]) #[[ATTR4:[0-9]+]] {
+// CHECK-SAME: <9 x i8> noundef [[A:%.*]]) #[[ATTR3:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca [9 x i8], align 1
// CHECK-NEXT: store <9 x i8> [[A]], ptr [[A_ADDR]], align 1
@@ -111,7 +117,7 @@ i8x3x3 m1(i8x3x3 a) {
}
// CHECK-LABEL: define dso_local noundef <9 x i32> @_Z2m2u11matrix_typeILm3ELm3EDB32_E(
-// CHECK-SAME: <9 x i32> noundef [[A:%.*]]) #[[ATTR5:[0-9]+]] {
+// CHECK-SAME: <9 x i32> noundef [[A:%.*]]) #[[ATTR4:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca [9 x i32], align 4
// CHECK-NEXT: store <9 x i32> [[A]], ptr [[A_ADDR]], align 4
@@ -125,7 +131,7 @@ i32x3x3 m2(i32x3x3 a) {
}
// CHECK-LABEL: define dso_local noundef <9 x i512> @_Z2m3u11matrix_typeILm3ELm3EDB512_E(
-// CHECK-SAME: <9 x i512> noundef [[A:%.*]]) #[[ATTR6:[0-9]+]] {
+// CHECK-SAME: <9 x i512> noundef [[A:%.*]]) #[[ATTR5:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca [9 x i512], align 8
// CHECK-NEXT: store <9 x i512> [[A]], ptr [[A_ADDR]], align 8
@@ -139,7 +145,7 @@ i512x3x3 m3(i512x3x3 a) {
}
// CHECK-LABEL: define dso_local noundef <9 x i4> @_Z2m4u11matrix_typeILm3ELm3EDB4_E(
-// CHECK-SAME: <9 x i4> noundef [[A:%.*]]) #[[ATTR7:[0-9]+]] {
+// CHECK-SAME: <9 x i4> noundef [[A:%.*]]) #[[ATTR6:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca [9 x i4], align 1
// CHECK-NEXT: store <9 x i4> [[A]], ptr [[A_ADDR]], align 1
diff --git a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
new file mode 100644
index 0000000000000..4f2ae00883461
--- /dev/null
+++ b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=SYSV
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=PS
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclang-abi-compat=22 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=CLANG22
+
+typedef unsigned long long v4ull __attribute__((vector_size(32)));
+
+struct EmptyBase {};
+
+struct EmptyBaseThenVector : EmptyBase {
+ v4ull Data;
+};
+
+EmptyBaseThenVector return_empty_base_then_vector() {
+ return {};
+}
+
+unsigned long long pass_empty_base_then_vector(EmptyBaseThenVector X) {
+ return X.Data[0];
+}
+
+struct EmptyField {};
+
+struct EmptyFieldThenVector {
+ [[no_unique_address]] EmptyField E;
+ v4ull Data;
+};
+
+EmptyFieldThenVector return_empty_field_then_vector() {
+ return {};
+}
+
+unsigned long long pass_empty_field_then_vector(EmptyFieldThenVector X) {
+ return X.Data[0];
+}
+
+// Empty base classes are covered by CodeGen/X86/avx-cxx-record.cpp. This test
+// covers [[no_unique_address]] empty fields, which are not handled by the
+// legacy classifier.
+// SYSV-LABEL: define dso_local <4 x i64> @_Z30return_empty_field_then_vectorv()
+// SYSV-LABEL: define dso_local noundef i64 @_Z28pass_empty_field_then_vector20EmptyFieldThenVector(<4 x i64> %X.coerce)
+
+// PlayStation keeps the legacy ABI behavior for both cases.
+// PS-LABEL: define dso_local <4 x i64> @_Z29return_empty_base_then_vectorv()
+// PS-LABEL: define dso_local noundef i64 @_Z27pass_empty_base_then_vector19EmptyBaseThenVector(<4 x i64> %X.coerce)
+// PS-LABEL: define dso_local void @_Z30return_empty_field_then_vectorv(ptr dead_on_unwind noalias writable sret(%struct.EmptyFieldThenVector) align 32 %agg.result)
+// PS-LABEL: define dso_local noundef i64 @_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef byval(%struct.EmptyFieldThenVector) align 32 %X)
+
+// Clang 22 ABI compatibility mode keeps the legacy ABI behavior for
+// [[no_unique_address]] empty fields.
+// CLANG22-LABEL: define dso_local void @_Z30return_empty_field_then_vectorv(ptr dead_on_unwind noalias writable sret(%struct.EmptyFieldThenVector) align 32 %agg.result)
+// CLANG22-LABEL: define dso_local noundef i64 @_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef byval(%struct.EmptyFieldThenVector) align 32 %X)
>From f4836964b5720cd234fb83a3bcc9f9cb74b61cf9 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 5 Aug 2026 13:45:57 -0700
Subject: [PATCH 02/12] Update tests to target clang23 compatibility instead of
clang22
---
clang/test/CodeGen/X86/x86-illegal-vector.c | 6 +++---
clang/test/CodeGen/X86/x86-small-fp-vector-abi.c | 2 +-
clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c | 2 +-
clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/test/CodeGen/X86/x86-illegal-vector.c b/clang/test/CodeGen/X86/x86-illegal-vector.c
index abb863f28d945..90e651142b350 100644
--- a/clang/test/CodeGen/X86/x86-illegal-vector.c
+++ b/clang/test/CodeGen/X86/x86-illegal-vector.c
@@ -1,11 +1,11 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,MEMRET256,MEMRET512
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +sse2 -emit-llvm -o - -fclang-abi-compat=23 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,REGRET256,MEMRET512
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx -emit-llvm -o - -fclang-abi-compat=23 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - -fclang-abi-compat=22 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -target-feature +avx512f -emit-llvm -o - -fclang-abi-compat=23 | FileCheck %s --check-prefixes=REGRET128,REGRET256,REGRET512
#define __MM_MALLOC_H
#include <x86intrin.h>
diff --git a/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c b/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
index 66f0fec6ee247..5bc3d577f3d63 100644
--- a/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
+++ b/clang/test/CodeGen/X86/x86-small-fp-vector-abi.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -fclang-abi-compat=22 -emit-llvm -o - %s | FileCheck %s --check-prefix=LEGACY
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fclang-abi-compat=23 -emit-llvm -o - %s | FileCheck %s --check-prefix=LEGACY
typedef _Float16 v1hf __attribute__((vector_size(2)));
typedef _Float16 v2hf __attribute__((vector_size(4)));
diff --git a/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c b/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
index 6d0bf403879fb..518fc434f1e71 100644
--- a/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
+++ b/clang/test/CodeGen/X86/x86_64-atomic-vector-abi.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-feature +avx %s -emit-llvm -o - | FileCheck %s --check-prefix=AVX
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-feature +avx -fclang-abi-compat=22 %s -emit-llvm -o - | FileCheck %s --check-prefix=LEGACY
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-feature +avx -fclang-abi-compat=23 %s -emit-llvm -o - | FileCheck %s --check-prefix=LEGACY
struct F3 {
float a[3];
diff --git a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
index 4f2ae00883461..235a8f11d31e7 100644
--- a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
+++ b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=SYSV
// RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=PS
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclang-abi-compat=22 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=CLANG22
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclang-abi-compat=23 -emit-llvm -o - %s -target-feature +avx | FileCheck %s --check-prefix=CLANG22
typedef unsigned long long v4ull __attribute__((vector_size(32)));
>From beff7d16b8edad6bc0c0f8fba33be0f2206bdcfc Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 5 Aug 2026 13:46:09 -0700
Subject: [PATCH 03/12] remove un-needed header includes
---
clang/lib/CodeGen/Targets/X86.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index a4334f40374fe..4e8f18b427565 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -13,8 +13,6 @@
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/Support/ErrorHandling.h"
-#include <cstdint>
using namespace clang;
using namespace clang::CodeGen;
>From ebd929378fe7451184786ffb6ec8783f7d011cc5 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 5 Aug 2026 13:46:59 -0700
Subject: [PATCH 04/12] Fix test to expect illegal vectors return in mem
---
clang/test/CodeGen/target-avx-function-abi.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/test/CodeGen/target-avx-function-abi.c b/clang/test/CodeGen/target-avx-function-abi.c
index 6ea0fd39a29f1..12e1bec8900fc 100644
--- a/clang/test/CodeGen/target-avx-function-abi.c
+++ b/clang/test/CodeGen/target-avx-function-abi.c
@@ -24,10 +24,12 @@ __attribute__((target("avx512f"))) v16f call_ptr_l512(v16f x) {
return fp(x);
}
-// SYSV-LABEL: define dso_local <8 x float> @g256(
+// SYSV-LABEL: define dso_local void @g256(
+// SYSV: sret(<8 x float>) align 32
// SYSV: byval(<8 x float>) align 32
-// SYSV-LABEL: define dso_local <16 x float> @g512(
+// SYSV-LABEL: define dso_local void @g512(
+// SYSV: sret(<16 x float>) align 64
// SYSV: byval(<16 x float>) align 64
// SYSV-LABEL: define dso_local <8 x float> @l256(<8 x float> noundef %x)
>From 3d18117d4eeee4f69f5577008bbf3be17cbeaf29 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Fri, 4 Sep 2026 10:44:57 -0700
Subject: [PATCH 05/12] clarify comment about int128 vectors
---
clang/lib/CodeGen/Targets/X86.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4e8f18b427565..43efa0b932b28 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2109,10 +2109,10 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
return;
}
- // Vectors of __int128 wider than 128 bits classify as MEMORY: each
- // __int128 lane contributes two INTEGER eightbytes, and post-merge cleanup
- // only keeps wider objects in registers when they are SSE followed by
- // SSEUp.
+ // The psABI does not specify how arbitrary GNU vector extension types such
+ // as vectors of __int128 are classified. Match GCC, which passes vectors
+ // of __int128 wider than 128 bits in memory on platforms that opt in to
+ // this compatibility behavior.
if (passInt128VectorsInMem() && Size > 128 &&
(ElementType->isSpecificBuiltinType(BuiltinType::Int128) ||
ElementType->isSpecificBuiltinType(BuiltinType::UInt128))) {
>From 082c8b1e4435e4dcebc1c635fd752529b8a63035 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Fri, 4 Sep 2026 10:58:27 -0700
Subject: [PATCH 06/12] Port PR 216777 to new classification algorithm
---
clang/lib/CodeGen/Targets/X86.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 43efa0b932b28..4e896aa676047 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2303,8 +2303,9 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx);
bool BitField = i->isBitField();
- // Ignore padding bit-fields.
- if (BitField && i->isUnnamedBitField())
+ // Ignore zero-length bit-fields. Other unnamed bit-fields are real
+ // storage and classify like named ones, matching GCC.
+ if (BitField && i->isZeroLengthBitField())
continue;
bool IsInMemory =
@@ -2326,7 +2327,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// structure to be passed in memory even if unaligned, and
// therefore they can straddle an eightbyte.
if (BitField) {
- assert(!i->isUnnamedBitField());
+ assert(!i->isZeroLengthBitField());
uint64_t BitSize = i->getBitWidthValue();
for (uint64_t BitOffset = Offset, End = Offset + BitSize;
BitOffset < End;
>From af205b6b9a36a380a3412013bdf99f66ee73bf93 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sat, 12 Sep 2026 21:16:11 -0700
Subject: [PATCH 07/12] fixup tests after rebase
---
clang/test/CodeGen/X86/x86_64-atomic-abi.c | 23 ++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/X86/x86_64-atomic-abi.c b/clang/test/CodeGen/X86/x86_64-atomic-abi.c
index c5e20e9c5ff33..ada36a7cc7cdc 100644
--- a/clang/test/CodeGen/X86/x86_64-atomic-abi.c
+++ b/clang/test/CodeGen/X86/x86_64-atomic-abi.c
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=CLANG24
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fclang-abi-compat=23 %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fexperimental-abi-lowering %s -o - | FileCheck %s
struct Floats {
@@ -19,6 +20,8 @@ void take_floats(struct Floats s) {}
// CHECK-LABEL: define dso_local void @take_floats(
// CHECK-SAME: <2 x float> %{{.*}})
+// CLANG24-LABEL: define dso_local void @take_floats(
+// CLANG24-SAME: <2 x float> %{{.*}})
// A scalar atomic still has the value type's evaluation kind, so it is a
// direct float argument.
@@ -26,27 +29,36 @@ void take_atomic_float(_Atomic(float) value) {}
// CHECK-LABEL: define dso_local void @take_atomic_float(
// CHECK-SAME: float %{{.*}})
+// CLANG24-LABEL: define dso_local void @take_atomic_float(
+// CLANG24-SAME: float %{{.*}})
-// Atomic fields classify Memory rather than inheriting the underlying float's
-// SSE class, so the record is passed on the stack.
+// The experimental classifier classifies atomic fields as Memory, so the
+// record is passed on the stack. The Clang 24 classifier instead classifies
+// each atomic field as its underlying float type, producing an SSE class.
void take_atomic_floats(struct AtomicFloats s) {}
// CHECK-LABEL: define dso_local void @take_atomic_floats(
// CHECK-SAME: ptr noundef byval(%struct.AtomicFloats) align 8 %{{.*}})
+// CLANG24-LABEL: define dso_local void @take_atomic_floats(
+// CLANG24-SAME: <2 x float> %{{.*}})
// The same classification applies when that record is nested.
void take_nested_atomic_floats(struct NestedAtomicFloats s) {}
// CHECK-LABEL: define dso_local void @take_nested_atomic_floats(
// CHECK-SAME: ptr noundef byval(%struct.NestedAtomicFloats) align 8 %{{.*}})
+// CLANG24-LABEL: define dso_local void @take_nested_atomic_floats(
+// CLANG24-SAME: <2 x float> %{{.*}})
// Atomic types preserve any size inflation relative to their value type.
void take_padded_atomic(_Atomic(ThreeBytes) value) {}
// CHECK-LABEL: define dso_local void @take_padded_atomic(
// CHECK-SAME: ptr noundef byval({ %struct.ThreeBytes, [1 x i8] }) align 8
+// CLANG24-LABEL: define dso_local void @take_padded_atomic(
+// CLANG24-SAME: ptr noundef byval({ %struct.ThreeBytes, [1 x i8] }) align 8
-// Aggregate returns use the same Memory classification.
+// Return classification follows the same distinction between classifiers.
struct AtomicFloats return_atomic_floats(void);
void call_return_atomic_floats(void) { return_atomic_floats(); }
@@ -55,3 +67,6 @@ void call_return_atomic_floats(void) { return_atomic_floats(); }
// CHECK-SAME: ptr dead_on_unwind writable sret(%struct.AtomicFloats) align 4
// CHECK: declare void @return_atomic_floats(
// CHECK-SAME: ptr dead_on_unwind writable sret(%struct.AtomicFloats) align 4)
+// CLANG24-LABEL: define dso_local void @call_return_atomic_floats(
+// CLANG24: call <2 x float> @return_atomic_floats()
+// CLANG24: declare <2 x float> @return_atomic_floats()
>From 234a61d570b5e1e34fa346211440b54d1d420a80 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sat, 12 Sep 2026 21:16:41 -0700
Subject: [PATCH 08/12] Fix bug with enum types dropping their classification
on the floor for the legacy classifier
---
clang/lib/CodeGen/Targets/X86.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4e896aa676047..4ee8707fb44fc 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2432,8 +2432,7 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
if (const auto *ED = Ty->getAsEnumDecl()) {
// Classify the underlying integer type.
- SmallVector<Class> IntEightBytes = {Lo, Hi};
- classifyClang23(ED->getIntegerType(), OffsetBase, IntEightBytes, isNamedArg,
+ classifyClang23(ED->getIntegerType(), OffsetBase, EightBytes, isNamedArg,
IsRegCall);
return;
}
>From dd3be89a9239d8cd1153a4b3865faabc9470c022 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sun, 13 Sep 2026 07:26:08 -0700
Subject: [PATCH 09/12] WIP on factoring out common logic between clang23 and
clang24 classifiers
---
clang/lib/CodeGen/Targets/X86.cpp | 420 ++++++++++++++----------------
1 file changed, 193 insertions(+), 227 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4ee8707fb44fc..bb7fe841f0334 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1229,6 +1229,18 @@ class X86_64ABIInfo : public ABIInfo {
Memory
};
+ struct ClassPair {
+ Class Lo = NoClass;
+ Class Hi = NoClass;
+ };
+
+ struct VectorTypeInfo {
+ QualType ElementType;
+ bool IsSingleElement;
+ bool IsFPElement;
+ bool IsInt128Element;
+ };
+
/// merge - Implement the X86_64 ABI merging algorithm.
///
/// Merge an accumulating classification \arg Accum with a field
@@ -1240,32 +1252,45 @@ class X86_64ABIInfo : public ABIInfo {
/// should just return Memory for the aggregate).
static Class merge(Class Accum, Class Field);
- /// classify - Determine the x86_64 register classes in which the
- /// given type T should be passed.
- ///
- /// \param Lo - The classification for the parts of the type
- /// residing in the low word of the containing object.
- ///
- /// \param Hi - The classification for the parts of the type
- /// residing in the high word of the containing object.
- ///
- /// \param OffsetBase - The bit offset of this type in the
- /// containing object. Some parameters are classified different
- /// depending on whether they straddle an eightbyte boundary.
- ///
- /// \param isNamedArg - Whether the argument in question is a "named"
- /// argument, as used in AMD64-ABI 3.5.7.
- ///
- /// \param IsRegCall - Whether the calling conversion is regcall.
- ///
- /// If a word is unused its result will be NoClass; if a type should
- /// be passed in Memory then at least the classification of \arg Lo
- /// will be Memory.
+ /// Return the classes intrinsic to the builtin type \param BT. The caller is
+ /// responsible for placing them at the type's offset.
+ ClassPair getBuiltinTypeClassification(const BuiltinType *BT) const;
+
+ /// Return the classes intrinsic to the complex type \param CT. The caller is
+ /// responsible for boundary splitting and class placement.
+ ClassPair getComplexTypeClassification(const ComplexType *CT) const;
+
+ /// Return the classes intrinsic to the bit-precise integer type \param BT.
+ /// The caller is responsible for class placement.
+ static ClassPair getBitIntTypeClassification(const BitIntType *BT);
+
+ /// Collect the canonical element type and shared classification properties
+ /// of the vector \param VT or matrix \param MT. At least one must be
+ /// non-null.
+ VectorTypeInfo getVectorTypeInfo(const VectorType *VT,
+ const MatrixType *MT = nullptr) const;
+
+ /// Return true when \param RT must be passed in memory without inspecting its
+ /// bases or fields because of its size, record ABI, or flexible array member.
+ bool isRecordPassedInMemory(const RecordType *RT, uint64_t Size) const;
+
+ /// Determine the x86_64 register classes used to pass or return \p T.
///
- /// The \arg Lo class will be NoClass iff the argument is ignored.
+ /// Each element of \p EightBytes describes an eightbyte in the containing
+ /// object. The Clang 24 classifier can produce an entry for every eightbyte,
+ /// while the legacy classifier represents the result using its first two
+ /// entries. NoClass denotes an unused eightbyte and Memory denotes a value
+ /// that must be passed in memory.
///
- /// If the \arg Lo class is ComplexX87, then the \arg Hi class will
- /// also be ComplexX87.
+ /// \param T The type to classify.
+ /// \param OffsetBase The bit offset of \p T in its containing object.
+ /// Classification can depend on whether the type straddles an eightbyte
+ /// boundary.
+ /// \param EightBytes The resulting eightbyte classes.
+ /// \param isNamedArg Whether the argument is named rather than part of a
+ /// variadic argument list, as used in AMD64-ABI 3.5.7.
+ /// \param isRegCall Whether the calling convention is regcall. Regcall uses
+ /// the legacy classifier.
void classify(QualType T, uint64_t OffsetBase,
SmallVectorImpl<Class> &EightBytes, bool isNamedArg,
bool isRegCall = false) const;
@@ -1871,6 +1896,108 @@ X86_64ABIInfo::Class X86_64ABIInfo::merge(Class Accum, Class Field) {
return SSE;
}
+X86_64ABIInfo::ClassPair
+X86_64ABIInfo::getBuiltinTypeClassification(const BuiltinType *BT) const {
+ BuiltinType::Kind K = BT->getKind();
+
+ if (K == BuiltinType::Void)
+ return {};
+ if (K == BuiltinType::Int128 || K == BuiltinType::UInt128)
+ return {Integer, Integer};
+ if (K >= BuiltinType::Bool && K <= BuiltinType::LongLong)
+ return {Integer};
+ if (K == BuiltinType::Float || K == BuiltinType::Double ||
+ K == BuiltinType::Float16 || K == BuiltinType::BFloat16)
+ return {SSE};
+ if (K == BuiltinType::Float128)
+ return {SSE, SSEUp};
+ if (K == BuiltinType::LongDouble) {
+ const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
+ if (LDF == &llvm::APFloat::IEEEquad())
+ return {SSE, SSEUp};
+ if (LDF == &llvm::APFloat::x87DoubleExtended())
+ return {X87, X87Up};
+ if (LDF == &llvm::APFloat::IEEEdouble())
+ return {SSE};
+ llvm_unreachable("unexpected long double representation!");
+ }
+
+ return {Memory};
+}
+
+X86_64ABIInfo::ClassPair
+X86_64ABIInfo::getComplexTypeClassification(const ComplexType *CT) const {
+ QualType ET = getContext().getCanonicalType(CT->getElementType());
+ uint64_t Size = getContext().getTypeSize(QualType(CT, 0));
+
+ if (ET->isIntegralOrEnumerationType()) {
+ if (Size <= 64)
+ return {Integer};
+ if (Size <= 128)
+ return {Integer, Integer};
+ llvm_unreachable("unexpected complex integer type size");
+ }
+ if (ET->isFloat16Type() || ET == getContext().FloatTy || ET->isBFloat16Type())
+ return {SSE};
+ if (ET == getContext().DoubleTy)
+ return {SSE, SSE};
+ if (ET == getContext().LongDoubleTy) {
+ const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
+ if (LDF == &llvm::APFloat::IEEEquad())
+ return {Memory};
+ if (LDF == &llvm::APFloat::x87DoubleExtended())
+ return {ComplexX87};
+ if (LDF == &llvm::APFloat::IEEEdouble())
+ return {SSE, SSE};
+ llvm_unreachable("unexpected long double representation!");
+ }
+ if (ET->isFloat128Type())
+ return {Memory};
+ llvm_unreachable("unexpected complex element type");
+}
+
+X86_64ABIInfo::ClassPair
+X86_64ABIInfo::getBitIntTypeClassification(const BitIntType *BT) {
+ if (BT->getNumBits() <= 64)
+ return {Integer};
+ if (BT->getNumBits() <= 128)
+ return {Integer, Integer};
+ return {Memory};
+}
+
+X86_64ABIInfo::VectorTypeInfo
+X86_64ABIInfo::getVectorTypeInfo(const VectorType *VT,
+ const MatrixType *MT) const {
+ assert((VT || MT) && "expected a vector or matrix type");
+ QualType ElementType = getContext().getCanonicalType(
+ VT ? VT->getElementType() : MT->getElementType());
+ bool IsFPElement = ElementType->isFloat16Type() ||
+ ElementType->isBFloat16Type() ||
+ ElementType == getContext().FloatTy ||
+ ElementType == getContext().DoubleTy;
+ bool IsInt128Element =
+ ElementType->isSpecificBuiltinType(BuiltinType::Int128) ||
+ ElementType->isSpecificBuiltinType(BuiltinType::UInt128);
+ return {ElementType, VT && VT->getNumElements() == 1, IsFPElement,
+ IsInt128Element};
+}
+
+bool X86_64ABIInfo::isRecordPassedInMemory(const RecordType *RT,
+ uint64_t Size) const {
+ // AMD64-ABI 3.2.3p2: Rule 1. Objects larger than eight eightbytes have class
+ // MEMORY.
+ if (Size > 512)
+ return true;
+
+ // Rule 2. C++ objects with a non-trivial copy constructor or destructor are
+ // passed by invisible reference.
+ if (getRecordArgABI(RT, getCXXABI()))
+ return true;
+
+ // Assume variable sized types are passed in memory.
+ return RT->getDecl()->getDefinitionOrSelf()->hasFlexibleArrayMember();
+}
+
void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase,
SmallVectorImpl<Class> &EightBytes,
bool isNamedArg, bool IsRegCall) const {
@@ -1958,6 +2085,17 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
SetEightByte(llvm::alignDown(Offset, uint64_t(64)) + 64, HiClass);
};
+ auto SetClassPair = [&](uint64_t Offset, ClassPair Classes) {
+ if (Classes.Lo == Memory || Classes.Hi == Memory) {
+ ClassifyAsMemory();
+ return;
+ }
+ if (Classes.Lo != NoClass)
+ SetEightByte(Offset, Classes.Lo);
+ if (Classes.Hi != NoClass)
+ SetEightByte(llvm::alignDown(Offset, uint64_t(64)) + 64, Classes.Hi);
+ };
+
auto SetTouchedEightBytes = [&](uint64_t Size, Class C) {
for (uint64_t Offset = OffsetBase, End = OffsetBase + Size; Offset < End;
Offset = llvm::alignDown(Offset, uint64_t(64)) + 64)
@@ -2002,40 +2140,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// Built In Types
if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
- BuiltinType::Kind K = BT->getKind();
-
- if (K == BuiltinType::Void)
- return;
- if (K == BuiltinType::Int128 || K == BuiltinType::UInt128) {
- SetAdjacentEightBytes(OffsetBase, Integer, Integer);
- return;
- }
- if (K >= BuiltinType::Bool && K <= BuiltinType::LongLong) {
- SetEightByte(OffsetBase, Integer);
- return;
- }
- if (K == BuiltinType::Float || K == BuiltinType::Double ||
- K == BuiltinType::Float16 || K == BuiltinType::BFloat16) {
- SetEightByte(OffsetBase, SSE);
- return;
- }
- if (K == BuiltinType::Float128) {
- SetAdjacentEightBytes(OffsetBase, SSE, SSEUp);
- return;
- }
- if (K == BuiltinType::LongDouble) {
- const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
- if (LDF == &llvm::APFloat::IEEEquad())
- SetAdjacentEightBytes(OffsetBase, SSE, SSEUp);
- else if (LDF == &llvm::APFloat::x87DoubleExtended())
- SetAdjacentEightBytes(OffsetBase, X87, X87Up);
- else if (LDF == &llvm::APFloat::IEEEdouble())
- SetEightByte(OffsetBase, SSE);
- else
- llvm_unreachable("unexpected long double representation!");
- return;
- }
- ClassifyAsMemory();
+ SetClassPair(OffsetBase, getBuiltinTypeClassification(BT));
return;
}
@@ -2064,25 +2169,8 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// same element type.
const VectorType *VT = Ty->getAs<VectorType>();
const MatrixType *MT = Ty->getAs<MatrixType>();
- bool IsVectorOrMatrix = VT != nullptr || MT != nullptr;
- if (IsVectorOrMatrix) {
- QualType ElementType;
- bool IsSingleElementVector = false;
- if (VT) {
- ElementType = VT->getElementType();
- IsSingleElementVector = VT->getNumElements() == 1;
- } else if (MT) {
- ElementType = MT->getElementType();
- } else {
- llvm_unreachable("invalid vector or matrix type");
- }
- ElementType = getContext().getCanonicalType(ElementType);
-
- auto IsFPVectorElement = [&]() {
- return ElementType->isFloat16Type() || ElementType->isBFloat16Type() ||
- ElementType == getContext().FloatTy ||
- ElementType == getContext().DoubleTy;
- };
+ if (VT || MT) {
+ VectorTypeInfo Info = getVectorTypeInfo(VT, MT);
auto SetEightByteClasses = [&](Class FirstClass, Class RestClass) {
// Vector objects should not start partway through one eightbyte and
@@ -2102,7 +2190,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
SetEightByte(Offset, RestClass);
};
- if (IsSingleElementVector && IsFPVectorElement()) {
+ if (Info.IsSingleElement && Info.IsFPElement) {
// GCC passes single-element floating-point vectors in memory. This is a
// compatibility rule, not a psABI classification rule.
ClassifyAsMemory();
@@ -2113,9 +2201,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// as vectors of __int128 are classified. Match GCC, which passes vectors
// of __int128 wider than 128 bits in memory on platforms that opt in to
// this compatibility behavior.
- if (passInt128VectorsInMem() && Size > 128 &&
- (ElementType->isSpecificBuiltinType(BuiltinType::Int128) ||
- ElementType->isSpecificBuiltinType(BuiltinType::UInt128))) {
+ if (passInt128VectorsInMem() && Size > 128 && Info.IsInt128Element) {
ClassifyAsMemory();
return;
}
@@ -2131,7 +2217,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// 4 bytes - <4 x char>, <2 x short>, <1 x int>
// 2 bytes - <2 x char>, <1 x short>
// 1 byte - <1 x char>
- Class C = IsFPVectorElement() ? SSE : Integer;
+ Class C = Info.IsFPElement ? SSE : Integer;
SetEightByteClasses(C, C);
} else if (Size == 64) {
@@ -2150,35 +2236,11 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// Complex types
if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
- QualType ET = getContext().getCanonicalType(CT->getElementType());
-
- uint64_t Size = getContext().getTypeSize(Ty);
- if (ET->isIntegralOrEnumerationType()) {
- if (Size <= 64)
- SetEightByte(OffsetBase, Integer);
- else if (Size <= 128)
- SetAdjacentEightBytes(OffsetBase, Integer, Integer);
- } else if (ET->isFloat16Type() || ET == getContext().FloatTy ||
- ET->isBFloat16Type()) {
- SetEightByte(OffsetBase, SSE);
- } else if (ET == getContext().DoubleTy) {
- SetAdjacentEightBytes(OffsetBase, SSE, SSE);
- } else if (ET == getContext().LongDoubleTy) {
- const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
- if (LDF == &llvm::APFloat::IEEEquad())
- ClassifyAsMemory();
- else if (LDF == &llvm::APFloat::x87DoubleExtended())
- SetEightByte(OffsetBase, ComplexX87);
- else if (LDF == &llvm::APFloat::IEEEdouble())
- SetAdjacentEightBytes(OffsetBase, SSE, SSE);
- else
- llvm_unreachable("unexpected long double representation!");
- } else if (ET->isFloat128Type()) {
- ClassifyAsMemory();
- }
+ SetClassPair(OffsetBase, getComplexTypeClassification(CT));
// If this complex type crosses an eightbyte boundary then it
// should be split.
+ QualType ET = getContext().getCanonicalType(CT->getElementType());
uint64_t EB_Real = (OffsetBase) / 64;
uint64_t EB_Imag = (OffsetBase + getContext().getTypeSize(ET)) / 64;
if (EightBytes[EB_Imag] == NoClass && EB_Real != EB_Imag)
@@ -2190,15 +2252,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// BitInt Types
if (const auto *EITy = Ty->getAs<BitIntType>()) {
- if (EITy->getNumBits() <= 64)
- SetEightByte(OffsetBase, Integer);
- else if (EITy->getNumBits() <= 128)
- SetAdjacentEightBytes(OffsetBase, Integer, Integer);
- else {
- // Larger values need to get passed in memory.
- ClassifyAsMemory();
- }
-
+ SetClassPair(OffsetBase, getBitIntTypeClassification(EITy));
return;
}
@@ -2243,31 +2297,12 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// CXX Record Type
if (const RecordType *RT = Ty->getAsCanonical<RecordType>()) {
- // uint64_t Size = getContext().getTypeSize(Ty);
-
- // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
- // than eight eightbytes, ..., it has class MEMORY.
- if (Size > 512) {
- ClassifyAsMemory();
- return;
- }
-
- // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial
- // copy constructor or a non-trivial destructor, it is passed by
- // invisible reference.
- if (getRecordArgABI(RT, getCXXABI())) {
+ if (isRecordPassedInMemory(RT, Size)) {
ClassifyAsMemory();
return;
}
const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
-
- // Assume variable sized types are passed in memory.
- if (RD->hasFlexibleArrayMember()) {
- ClassifyAsMemory();
- return;
- }
-
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
// If this is a C++ record, classify the bases first.
@@ -2350,10 +2385,6 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
SmallVectorImpl<Class> &EightBytes,
bool isNamedArg, bool IsRegCall) const {
- // FIXME: This code can be simplified by introducing a simple value class
- // for Class pairs with appropriate constructor methods for the various
- // situations.
-
// FIXME: Some of the split computations are wrong; unaligned vectors
// shouldn't be passed in registers for example, so there is no chance they
// can straddle an eightbyte. Verify & simplify.
@@ -2396,37 +2427,17 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
Class &Current = OffsetBase < 64 ? Lo : Hi;
Current = Memory;
- if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
- BuiltinType::Kind k = BT->getKind();
-
- if (k == BuiltinType::Void) {
- Current = NoClass;
- } else if (k == BuiltinType::Int128 || k == BuiltinType::UInt128) {
- Lo = Integer;
- Hi = Integer;
- } else if (k >= BuiltinType::Bool && k <= BuiltinType::LongLong) {
- Current = Integer;
- } else if (k == BuiltinType::Float || k == BuiltinType::Double ||
- k == BuiltinType::Float16 || k == BuiltinType::BFloat16) {
- Current = SSE;
- } else if (k == BuiltinType::Float128) {
- Lo = SSE;
- Hi = SSEUp;
- } else if (k == BuiltinType::LongDouble) {
- const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
- if (LDF == &llvm::APFloat::IEEEquad()) {
- Lo = SSE;
- Hi = SSEUp;
- } else if (LDF == &llvm::APFloat::x87DoubleExtended()) {
- Lo = X87;
- Hi = X87Up;
- } else if (LDF == &llvm::APFloat::IEEEdouble()) {
- Current = SSE;
- } else
- llvm_unreachable("unexpected long double representation!");
+ auto SetClassPair = [&](ClassPair Classes) {
+ if (Classes.Hi == NoClass) {
+ Current = Classes.Lo;
+ return;
}
- // FIXME: _Decimal32 and _Decimal64 are SSE.
- // FIXME: _float128 and _Decimal128 are (SSE, SSEUp).
+ Lo = Classes.Lo;
+ Hi = Classes.Hi;
+ };
+
+ if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
+ SetClassPair(getBuiltinTypeClassification(BT));
return;
}
@@ -2467,6 +2478,7 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
if (const VectorType *VT = Ty->getAs<VectorType>()) {
uint64_t Size = getContext().getTypeSize(VT);
+ VectorTypeInfo Info = getVectorTypeInfo(VT);
if (Size == 1 || Size == 8 || Size == 16 || Size == 32) {
// gcc passes the following as integer:
// 4 bytes - <4 x char>, <2 x short>, <1 x int>, <1 x float>
@@ -2481,20 +2493,18 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
if (EB_Lo != EB_Hi)
Hi = Lo;
} else if (Size == 64) {
- QualType ElementType = VT->getElementType();
-
// gcc passes <1 x double> in memory. :(
- if (ElementType->isSpecificBuiltinType(BuiltinType::Double))
+ if (Info.ElementType->isSpecificBuiltinType(BuiltinType::Double))
return;
// gcc passes <1 x long long> as SSE but clang used to unconditionally
// pass them as integer. For platforms where clang is the de facto
// platform compiler, we must continue to use integer.
if (!classifyIntegerMMXAsSSE() &&
- (ElementType->isSpecificBuiltinType(BuiltinType::LongLong) ||
- ElementType->isSpecificBuiltinType(BuiltinType::ULongLong) ||
- ElementType->isSpecificBuiltinType(BuiltinType::Long) ||
- ElementType->isSpecificBuiltinType(BuiltinType::ULong)))
+ (Info.ElementType->isSpecificBuiltinType(BuiltinType::LongLong) ||
+ Info.ElementType->isSpecificBuiltinType(BuiltinType::ULongLong) ||
+ Info.ElementType->isSpecificBuiltinType(BuiltinType::Long) ||
+ Info.ElementType->isSpecificBuiltinType(BuiltinType::ULong)))
Current = Integer;
else
Current = SSE;
@@ -2505,12 +2515,8 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
Hi = Lo;
} else if (Size == 128 ||
(isNamedArg && Size <= getNativeVectorSizeForAVXABI(AVXLevel))) {
- QualType ElementType = VT->getElementType();
-
// gcc passes 256 and 512 bit <X x __int128> vectors in memory. :(
- if (passInt128VectorsInMem() && Size != 128 &&
- (ElementType->isSpecificBuiltinType(BuiltinType::Int128) ||
- ElementType->isSpecificBuiltinType(BuiltinType::UInt128)))
+ if (passInt128VectorsInMem() && Size != 128 && Info.IsInt128Element)
return;
// Arguments of 256-bits are split into four eightbyte chunks. The
@@ -2534,33 +2540,11 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
}
if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
- QualType ET = getContext().getCanonicalType(CT->getElementType());
-
- uint64_t Size = getContext().getTypeSize(Ty);
- if (ET->isIntegralOrEnumerationType()) {
- if (Size <= 64)
- Current = Integer;
- else if (Size <= 128)
- Lo = Hi = Integer;
- } else if (ET->isFloat16Type() || ET == getContext().FloatTy ||
- ET->isBFloat16Type()) {
- Current = SSE;
- } else if (ET == getContext().DoubleTy) {
- Lo = Hi = SSE;
- } else if (ET == getContext().LongDoubleTy) {
- const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
- if (LDF == &llvm::APFloat::IEEEquad())
- Current = Memory;
- else if (LDF == &llvm::APFloat::x87DoubleExtended())
- Current = ComplexX87;
- else if (LDF == &llvm::APFloat::IEEEdouble())
- Lo = Hi = SSE;
- else
- llvm_unreachable("unexpected long double representation!");
- }
+ SetClassPair(getComplexTypeClassification(CT));
// If this complex type crosses an eightbyte boundary then it
// should be split.
+ QualType ET = getContext().getCanonicalType(CT->getElementType());
uint64_t EB_Real = (OffsetBase) / 64;
uint64_t EB_Imag = (OffsetBase + getContext().getTypeSize(ET)) / 64;
if (Hi == NoClass && EB_Real != EB_Imag)
@@ -2570,11 +2554,7 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
}
if (const auto *EITy = Ty->getAs<BitIntType>()) {
- if (EITy->getNumBits() <= 64)
- Current = Integer;
- else if (EITy->getNumBits() <= 128)
- Lo = Hi = Integer;
- // Larger values need to get passed in memory.
+ SetClassPair(getBitIntTypeClassification(EITy));
return;
}
@@ -2632,24 +2612,10 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
if (const RecordType *RT = Ty->getAsCanonical<RecordType>()) {
uint64_t Size = getContext().getTypeSize(Ty);
-
- // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
- // than eight eightbytes, ..., it has class MEMORY.
- if (Size > 512)
- return;
-
- // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial
- // copy constructor or a non-trivial destructor, it is passed by invisible
- // reference.
- if (getRecordArgABI(RT, getCXXABI()))
+ if (isRecordPassedInMemory(RT, Size))
return;
const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
-
- // Assume variable sized types are passed in memory.
- if (RD->hasFlexibleArrayMember())
- return;
-
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
// Reset Lo class, this will be recomputed.
>From 73436c9cbb7635af2c3efa3b29b2b7a1ab594732 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sun, 13 Sep 2026 08:16:39 -0700
Subject: [PATCH 10/12] update comment to include open CL pipes
---
clang/lib/CodeGen/Targets/X86.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index bb7fe841f0334..93129a4e7b757 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2151,7 +2151,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
return;
}
- // Ptrs
+ // Pointers and OpenCL pipes have pointer-sized runtime representations.
if (Ty->hasPointerRepresentation() || Ty->isPipeType()) {
SetEightByte(OffsetBase, Integer);
return;
>From 8c47dd0fea211ed675f25cf0c36531f24c370a2c Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sun, 13 Sep 2026 13:07:45 -0700
Subject: [PATCH 11/12] factored out more common logic between the two
classification paths
---
clang/lib/CodeGen/Targets/X86.cpp | 200 +++++++++++-------------------
1 file changed, 75 insertions(+), 125 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 93129a4e7b757..721bd60219e35 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1256,20 +1256,31 @@ class X86_64ABIInfo : public ABIInfo {
/// responsible for placing them at the type's offset.
ClassPair getBuiltinTypeClassification(const BuiltinType *BT) const;
- /// Return the classes intrinsic to the complex type \param CT. The caller is
- /// responsible for boundary splitting and class placement.
- ClassPair getComplexTypeClassification(const ComplexType *CT) const;
+ /// Return the classes intrinsic to the complex type \param CT, including any
+ /// splitting required at \param Offset. The caller is responsible for class
+ /// placement.
+ ClassPair getComplexTypeClassification(const ComplexType *CT,
+ uint64_t Offset) const;
/// Return the classes intrinsic to the bit-precise integer type \param BT.
/// The caller is responsible for class placement.
static ClassPair getBitIntTypeClassification(const BitIntType *BT);
+ /// Return \param C for each eightbyte touched by the bit span beginning at
+ /// \param Offset. The result is relative to the first touched eightbyte.
+ static ClassPair getClassPairForSpan(uint64_t Offset, uint64_t Size, Class C);
+
/// Collect the canonical element type and shared classification properties
/// of the vector \param VT or matrix \param MT. At least one must be
/// non-null.
VectorTypeInfo getVectorTypeInfo(const VectorType *VT,
const MatrixType *MT = nullptr) const;
+ /// Return true when \param AT must be passed in memory because of the SysV
+ /// size limit or the alignment of its base. Regcall ignores the size limit.
+ bool isArrayPassedInMemory(const ConstantArrayType *AT, uint64_t Offset,
+ uint64_t Size, bool IsRegCall) const;
+
/// Return true when \param RT must be passed in memory without inspecting its
/// bases or fields because of its size, record ABI, or flexible array member.
bool isRecordPassedInMemory(const RecordType *RT, uint64_t Size) const;
@@ -1926,34 +1937,46 @@ X86_64ABIInfo::getBuiltinTypeClassification(const BuiltinType *BT) const {
}
X86_64ABIInfo::ClassPair
-X86_64ABIInfo::getComplexTypeClassification(const ComplexType *CT) const {
+X86_64ABIInfo::getComplexTypeClassification(const ComplexType *CT,
+ uint64_t Offset) const {
QualType ET = getContext().getCanonicalType(CT->getElementType());
+ uint64_t ElementSize = getContext().getTypeSize(ET);
uint64_t Size = getContext().getTypeSize(QualType(CT, 0));
+ ClassPair Classes;
if (ET->isIntegralOrEnumerationType()) {
if (Size <= 64)
- return {Integer};
- if (Size <= 128)
- return {Integer, Integer};
- llvm_unreachable("unexpected complex integer type size");
- }
- if (ET->isFloat16Type() || ET == getContext().FloatTy || ET->isBFloat16Type())
- return {SSE};
- if (ET == getContext().DoubleTy)
- return {SSE, SSE};
- if (ET == getContext().LongDoubleTy) {
+ Classes = {Integer};
+ else if (Size <= 128)
+ Classes = {Integer, Integer};
+ else
+ llvm_unreachable("unexpected complex integer type size");
+ } else if (ET->isFloat16Type() || ET == getContext().FloatTy ||
+ ET->isBFloat16Type()) {
+ Classes = {SSE};
+ } else if (ET == getContext().DoubleTy) {
+ Classes = {SSE, SSE};
+ } else if (ET == getContext().LongDoubleTy) {
const llvm::fltSemantics *LDF = &getTarget().getLongDoubleFormat();
if (LDF == &llvm::APFloat::IEEEquad())
- return {Memory};
- if (LDF == &llvm::APFloat::x87DoubleExtended())
- return {ComplexX87};
- if (LDF == &llvm::APFloat::IEEEdouble())
- return {SSE, SSE};
- llvm_unreachable("unexpected long double representation!");
+ Classes = {Memory};
+ else if (LDF == &llvm::APFloat::x87DoubleExtended())
+ Classes = {ComplexX87};
+ else if (LDF == &llvm::APFloat::IEEEdouble())
+ Classes = {SSE, SSE};
+ else
+ llvm_unreachable("unexpected long double representation!");
+ } else if (ET->isFloat128Type()) {
+ Classes = {Memory};
+ } else {
+ llvm_unreachable("unexpected complex element type");
}
- if (ET->isFloat128Type())
- return {Memory};
- llvm_unreachable("unexpected complex element type");
+
+ uint64_t EBReal = Offset / 64;
+ uint64_t EBImag = (Offset + ElementSize) / 64;
+ if (Classes.Hi == NoClass && EBReal != EBImag)
+ Classes.Hi = Classes.Lo;
+ return Classes;
}
X86_64ABIInfo::ClassPair
@@ -1965,6 +1988,13 @@ X86_64ABIInfo::getBitIntTypeClassification(const BitIntType *BT) {
return {Memory};
}
+X86_64ABIInfo::ClassPair
+X86_64ABIInfo::getClassPairForSpan(uint64_t Offset, uint64_t Size, Class C) {
+ assert(Size && Offset % 64 + Size <= 128 &&
+ "span must touch no more than two eightbytes");
+ return {C, Offset % 64 + Size > 64 ? C : NoClass};
+}
+
X86_64ABIInfo::VectorTypeInfo
X86_64ABIInfo::getVectorTypeInfo(const VectorType *VT,
const MatrixType *MT) const {
@@ -1982,6 +2012,16 @@ X86_64ABIInfo::getVectorTypeInfo(const VectorType *VT,
IsInt128Element};
}
+bool X86_64ABIInfo::isArrayPassedInMemory(const ConstantArrayType *AT,
+ uint64_t Offset, uint64_t Size,
+ bool IsRegCall) const {
+ // AMD64-ABI 3.2.3p2: Rule 1. Objects larger than eight eightbytes or
+ // containing unaligned fields have class MEMORY. Regcall does not impose the
+ // SysV size limit.
+ return (!IsRegCall && Size > 512) ||
+ Offset % getContext().getTypeAlign(AT->getElementType());
+}
+
bool X86_64ABIInfo::isRecordPassedInMemory(const RecordType *RT,
uint64_t Size) const {
// AMD64-ABI 3.2.3p2: Rule 1. Objects larger than eight eightbytes have class
@@ -2079,12 +2119,6 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
EightBytes[Offset / 64] = C;
};
- auto SetAdjacentEightBytes = [&](uint64_t Offset, Class LoClass,
- Class HiClass) {
- SetEightByte(Offset, LoClass);
- SetEightByte(llvm::alignDown(Offset, uint64_t(64)) + 64, HiClass);
- };
-
auto SetClassPair = [&](uint64_t Offset, ClassPair Classes) {
if (Classes.Lo == Memory || Classes.Hi == Memory) {
ClassifyAsMemory();
@@ -2096,12 +2130,6 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
SetEightByte(llvm::alignDown(Offset, uint64_t(64)) + 64, Classes.Hi);
};
- auto SetTouchedEightBytes = [&](uint64_t Size, Class C) {
- for (uint64_t Offset = OffsetBase, End = OffsetBase + Size; Offset < End;
- Offset = llvm::alignDown(Offset, uint64_t(64)) + 64)
- SetEightByte(Offset, C);
- };
-
// End Helpers
// Resize the EigthBytes vector if needed to fit the type
@@ -2146,7 +2174,6 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// Enums
if (const auto *ED = Ty->getAsEnumDecl()) {
- // Classify the underlying integer type.
classifyClang24(ED->getIntegerType(), OffsetBase, EightBytes, isNamedArg);
return;
}
@@ -2160,7 +2187,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// C++ pointer-to-member types have integer-class storage: data member
// pointers are one ptrdiff_t, and member function pointers are two.
if (Ty->isMemberPointerType()) {
- SetTouchedEightBytes(Size, Integer);
+ SetClassPair(OffsetBase, getClassPairForSpan(OffsetBase, Size, Integer));
return;
}
@@ -2236,17 +2263,7 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// Complex types
if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
- SetClassPair(OffsetBase, getComplexTypeClassification(CT));
-
- // If this complex type crosses an eightbyte boundary then it
- // should be split.
- QualType ET = getContext().getCanonicalType(CT->getElementType());
- uint64_t EB_Real = (OffsetBase) / 64;
- uint64_t EB_Imag = (OffsetBase + getContext().getTypeSize(ET)) / 64;
- if (EightBytes[EB_Imag] == NoClass && EB_Real != EB_Imag)
- SetAdjacentEightBytes(OffsetBase, EightBytes[EB_Real],
- EightBytes[EB_Real]);
-
+ SetClassPair(OffsetBase, getComplexTypeClassification(CT, OffsetBase));
return;
}
@@ -2258,26 +2275,12 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
// Arrays
if (const ConstantArrayType *AT = getContext().getAsConstantArrayType(Ty)) {
- // Arrays are treated like structures.
-
- // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
- // than eight eightbytes, ..., it has class MEMORY.
- if (Size > 512) {
- ClassifyAsMemory();
- return;
- }
-
- // AMD64-ABI 3.2.3p2: Rule 1. If ..., or it contains unaligned
- // fields, it has class MEMORY.
- //
- // Only need to check alignment of array base.
- if (OffsetBase % getContext().getTypeAlign(AT->getElementType())) {
+ if (isArrayPassedInMemory(AT, OffsetBase, Size,
+ /*IsRegCall=*/false)) {
ClassifyAsMemory();
return;
}
- // Otherwise implement simplified merge. We could be smarter about
- // this, but it isn't worth it and would be harder to verify.
uint64_t EltSize = getContext().getTypeSize(AT->getElementType());
uint64_t ArraySize = AT->getZExtSize();
@@ -2305,23 +2308,19 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
- // If this is a C++ record, classify the bases first.
+ // Classify bases followed by fields, merging each component's eightbyte
+ // classes. Bit-fields are handled directly because they may straddle
+ // eightbyte boundaries.
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
for (const auto &I : CXXRD->bases()) {
assert(!I.isVirtual() && !I.getType()->isDependentType() &&
"Unexpected base class!");
const auto *Base = I.getType()->castAsCXXRecordDecl();
- // Classify this field.
- //
- // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate exceeds
- // a single eightbyte, each is classified separately. Each
- // eightbyte gets initialized to class NO_CLASS.
SmallVector<Class> BaseEightBytes;
uint64_t Offset =
OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base));
classifyClang24(I.getType(), Offset % 64, BaseEightBytes, isNamedArg);
- // If the base is memory the whole record will be passed as memory
if (BaseEightBytes[0] == Memory) {
ClassifyAsMemory();
return;
@@ -2330,7 +2329,6 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
}
}
- // Classify the fields one at a time, merging the results.
unsigned idx = 0;
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
@@ -2345,22 +2343,13 @@ void X86_64ABIInfo::classifyClang24(QualType Ty, uint64_t OffsetBase,
bool IsInMemory =
Offset % getContext().getTypeAlign(i->getType().getCanonicalType());
- // Note, skip this test for bit-fields, see below.
+ // Bit-fields may be unaligned and are handled below.
if (!BitField && IsInMemory) {
ClassifyAsMemory();
PostMerge();
return;
}
- // Classify this field.
- //
- // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate
- // exceeds a single eightbyte, each is classified
- // separately. Each eightbyte gets initialized to class
- // NO_CLASS.
- // Bit-fields require special handling, they do not force the
- // structure to be passed in memory even if unaligned, and
- // therefore they can straddle an eightbyte.
if (BitField) {
assert(!i->isZeroLengthBitField());
uint64_t BitSize = i->getBitWidthValue();
@@ -2454,25 +2443,8 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
}
if (Ty->isMemberPointerType()) {
- if (Ty->isMemberFunctionPointerType()) {
- if (Has64BitPointers) {
- // If Has64BitPointers, this is an {i64, i64}, so classify both
- // Lo and Hi now.
- Lo = Hi = Integer;
- } else {
- // Otherwise, with 32-bit pointers, this is an {i32, i32}. If that
- // straddles an eightbyte boundary, Hi should be classified as well.
- uint64_t EB_FuncPtr = (OffsetBase) / 64;
- uint64_t EB_ThisAdj = (OffsetBase + 64 - 1) / 64;
- if (EB_FuncPtr != EB_ThisAdj) {
- Lo = Hi = Integer;
- } else {
- Current = Integer;
- }
- }
- } else {
- Current = Integer;
- }
+ SetClassPair(
+ getClassPairForSpan(OffsetBase, getContext().getTypeSize(Ty), Integer));
return;
}
@@ -2540,16 +2512,7 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
}
if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
- SetClassPair(getComplexTypeClassification(CT));
-
- // If this complex type crosses an eightbyte boundary then it
- // should be split.
- QualType ET = getContext().getCanonicalType(CT->getElementType());
- uint64_t EB_Real = (OffsetBase) / 64;
- uint64_t EB_Imag = (OffsetBase + getContext().getTypeSize(ET)) / 64;
- if (Hi == NoClass && EB_Real != EB_Imag)
- Hi = Lo;
-
+ SetClassPair(getComplexTypeClassification(CT, OffsetBase));
return;
}
@@ -2560,21 +2523,8 @@ void X86_64ABIInfo::classifyClang23(QualType Ty, uint64_t OffsetBase,
if (const ConstantArrayType *AT = getContext().getAsConstantArrayType(Ty)) {
// Arrays are treated like structures.
-
uint64_t Size = getContext().getTypeSize(Ty);
-
- // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger
- // than eight eightbytes, ..., it has class MEMORY.
- // regcall ABI doesn't have limitation to an object. The only limitation
- // is the free registers, which will be checked in computeInfo.
- if (!IsRegCall && Size > 512)
- return;
-
- // AMD64-ABI 3.2.3p2: Rule 1. If ..., or it contains unaligned
- // fields, it has class MEMORY.
- //
- // Only need to check alignment of array base.
- if (OffsetBase % getContext().getTypeAlign(AT->getElementType()))
+ if (isArrayPassedInMemory(AT, OffsetBase, Size, IsRegCall))
return;
// Otherwise implement simplified merge. We could be smarter about
>From 4c69804c5fa65e1fcb55bd0f789ccca6de362dd8 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Sun, 13 Sep 2026 13:08:35 -0700
Subject: [PATCH 12/12] Add a test for missing enum abi coverage
---
clang/test/CodeGen/X86/x86_64-enum-abi.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 clang/test/CodeGen/X86/x86_64-enum-abi.c
diff --git a/clang/test/CodeGen/X86/x86_64-enum-abi.c b/clang/test/CodeGen/X86/x86_64-enum-abi.c
new file mode 100644
index 0000000000000..f57b8611e8081
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-enum-abi.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fclang-abi-compat=23 -emit-llvm %s -o - | FileCheck %s
+
+enum E { A, B };
+
+struct EnumRecord {
+ enum E value;
+};
+
+// CHECK-LABEL: define dso_local void @take_enum_record(
+// CHECK-SAME: i32 %{{.*}})
+void take_enum_record(struct EnumRecord value) {}
+
+// CHECK-LABEL: define dso_local i32 @return_enum_record(
+struct EnumRecord return_enum_record(struct EnumRecord value) { return value; }
More information about the cfe-commits
mailing list