https://github.com/adams381 updated 
https://github.com/llvm/llvm-project/pull/230302

>From e3d27550ff6effab245f719b6ddfa45af250154d Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 8 Oct 2026 15:12:00 -0700
Subject: [PATCH 1/3] [CIR] Pass x87 long double vectors on x86_64

Vectors of x87 long double now go through x86_64 calling-convention
lowering instead of hitting NYI. The ABI library sizes their elements at
128 bits like clang does, so the signatures match classic codegen.

Unions are still moved as a value of their storage type, and a long
double stores only 10 of its 16 bytes. So a union holding an x87 value
next to another member stays NYI unless it's a plain long double and the
other members fit in those 10 bytes. That also stops a silent miscompile
of unions like `union { long double ld; char c[16]; }`.

Assisted-by: Cursor / Claude Opus 5.5
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  25 +-
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  16 +-
 .../Transforms/CallConvLoweringPass.cpp       | 107 ++++++-
 .../call-conv-lowering-x86_64-x87-vector.c    | 291 ++++++++++++++++++
 .../CodeGen/vector-logical-long-double.cpp    |  66 ++++
 .../abi-lowering/x86_64-aggregate-nyi.cir     | 114 ++++++-
 .../Transforms/abi-lowering/x86_64-vector.cir |  18 ++
 7 files changed, 602 insertions(+), 35 deletions(-)
 create mode 100644 
clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
 create mode 100644 clang/test/CIR/CodeGen/vector-logical-long-double.cpp

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 36d583cfe9fbed..36140601caab21 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -906,18 +906,35 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return cir::CmpOp::create(*this, loc, kind, lhs, rhs, fenv);
   }
 
+  /// Compares \p lhs and \p rhs element-wise.  Each result element is a
+  /// signed integer of the operand element's bit width, 80 for x87 long
+  /// double.
   cir::VecCmpOp createVecCompare(mlir::Location loc, cir::CmpOpKind kind,
                                  mlir::Value lhs, mlir::Value rhs) {
     VectorType vecCast = mlir::cast<VectorType>(lhs.getType());
     IntType integralTy =
         getSIntNTy(getCIRIntOrFloatBitWidth(vecCast.getElementType()));
-    VectorType integralVecTy =
-        cir::VectorType::get(integralTy, vecCast.getSize());
+    return createVecCompare(loc,
+                            cir::VectorType::get(integralTy, 
vecCast.getSize()),
+                            kind, lhs, rhs);
+  }
+
+  /// Compares \p lhs and \p rhs element-wise, producing \p resultTy, which
+  /// must be a vector of integers or bools with as many elements as \p lhs.
+  /// The result element width can then differ from the operand element's
+  /// bit width, as for x87 long double, whose result elements are 128 bits.
+  cir::VecCmpOp createVecCompare(mlir::Location loc, VectorType resultTy,
+                                 cir::CmpOpKind kind, mlir::Value lhs,
+                                 mlir::Value rhs) {
+    assert(mlir::cast<VectorType>(lhs.getType()).getSize() ==
+               resultTy.getSize() &&
+           cir::isIntOrBoolType(resultTy.getElementType()) &&
+           "vector compare result must be an int or bool vector of the "
+           "operand length");
     cir::FenvAttr fenv;
     if (cir::isFPOrVectorOfFPType(lhs.getType()))
       fenv = getConstrainedFPAttr();
-    return cir::VecCmpOp::create(*this, loc, integralVecTy, kind, lhs, rhs,
-                                 fenv);
+    return cir::VecCmpOp::create(*this, loc, resultTy, kind, lhs, rhs, fenv);
   }
 
   mlir::Value createIsNaN(mlir::Location loc, mlir::Value operand) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 836f27cf5345ef..59d14d724dbfc5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1428,8 +1428,10 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
       mlir::Value rhs = Visit(e->getRHS());
 
       auto cmpOpKind = cir::CmpOpKind::ne;
-      lhs = builder.createVecCompare(loc, cmpOpKind, lhs, zeroVec);
-      rhs = builder.createVecCompare(loc, cmpOpKind, rhs, zeroVec);
+      auto resultTy =
+          mlir::cast<cir::VectorType>(cgf.convertType(e->getType()));
+      lhs = builder.createVecCompare(loc, resultTy, cmpOpKind, lhs, zeroVec);
+      rhs = builder.createVecCompare(loc, resultTy, cmpOpKind, rhs, zeroVec);
       return builder.createAnd(loc, lhs, rhs);
     }
 
@@ -1474,8 +1476,10 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
       mlir::Value rhs = Visit(e->getRHS());
 
       auto cmpOpKind = cir::CmpOpKind::ne;
-      lhs = builder.createVecCompare(loc, cmpOpKind, lhs, zeroVec);
-      rhs = builder.createVecCompare(loc, cmpOpKind, rhs, zeroVec);
+      auto resultTy =
+          mlir::cast<cir::VectorType>(cgf.convertType(e->getType()));
+      lhs = builder.createVecCompare(loc, resultTy, cmpOpKind, lhs, zeroVec);
+      rhs = builder.createVecCompare(loc, resultTy, cmpOpKind, rhs, zeroVec);
       return builder.createOr(loc, lhs, rhs);
     }
 
@@ -3005,7 +3009,9 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const 
UnaryOperator *e) {
     mlir::Location loc = cgf.getLoc(e->getExprLoc());
     auto operVecTy = mlir::cast<cir::VectorType>(oper.getType());
     mlir::Value zeroVec = builder.getNullValue(operVecTy, loc);
-    return builder.createVecCompare(loc, cir::CmpOpKind::eq, oper, zeroVec);
+    auto resultTy = mlir::cast<cir::VectorType>(cgf.convertType(e->getType()));
+    return builder.createVecCompare(loc, resultTy, cir::CmpOpKind::eq, oper,
+                                    zeroVec);
   }
 
   // Compare operand to zero.
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 402cd09afd6370..beeab4bf4d5e3d 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -159,6 +159,55 @@ static bool hasIncompleteRecordByValue(mlir::Type ty) {
   return false;
 }
 
+/// Whether \p ty has the x87 80-bit format, wrapped in LongDoubleType or not.
+static bool isX87LongDouble(mlir::Type ty) {
+  auto fpTy = dyn_cast<cir::FPTypeInterface>(ty);
+  return fpTy &&
+         &fpTy.getFloatSemantics() == &llvm::APFloat::x87DoubleExtended();
+}
+
+/// Whether \p ty is, or holds by value, an x87 long double, alone or as the
+/// element of a _Complex or a vector.
+static bool holdsX87LongDouble(mlir::Type ty) {
+  if (isa<cir::FPTypeInterface>(ty))
+    return isX87LongDouble(ty);
+  if (auto complexTy = dyn_cast<cir::ComplexType>(ty))
+    return holdsX87LongDouble(complexTy.getElementType());
+  if (auto vecTy = dyn_cast<cir::VectorType>(ty))
+    return holdsX87LongDouble(vecTy.getElementType());
+  if (auto arrTy = dyn_cast<cir::ArrayType>(ty))
+    return arrTy.getSize() && holdsX87LongDouble(arrTy.getElementType());
+  if (auto recTy = dyn_cast<cir::RecordType>(ty))
+    return llvm::any_of(recTy.getMembers(), holdsX87LongDouble);
+  return false;
+}
+
+/// Returns how many bytes from the start of \p ty a value of it reaches.  A
+/// scalar reaches the end of its storage, a record the furthest end of its
+/// non-padding members, and an array the end of its last element, so padding
+/// at the end of a record does not count.
+static uint64_t getDataExtentInBytes(mlir::Type ty, const DataLayout &dl) {
+  if (auto recTy = dyn_cast<cir::RecordType>(ty)) {
+    uint64_t extent = 0;
+    for (auto [idx, memberTy, kind] :
+         llvm::enumerate(recTy.getMembers(), recTy.getMemberKinds())) {
+      if (kind == cir::RecordMemberKind::Pad)
+        continue;
+      extent = std::max(extent, recTy.getElementOffset(dl, idx) +
+                                    getDataExtentInBytes(memberTy, dl));
+    }
+    return extent;
+  }
+  if (auto arrTy = dyn_cast<cir::ArrayType>(ty)) {
+    if (!arrTy.getSize())
+      return 0;
+    mlir::Type elemTy = arrTy.getElementType();
+    return (arrTy.getSize() - 1) * dl.getTypeSize(elemTy).getFixedValue() +
+           getDataExtentInBytes(elemTy, dl);
+  }
+  return dl.getTypeSize(ty).getFixedValue();
+}
+
 /// The CIR types the x86_64 bridge handles.  Scalars: an integer up to 128
 /// bits (including `__int128`), a `_BitInt` of any width, pointer, vtable
 /// pointer, bool, void, or any floating-point type.  Aggregates: a complete
@@ -205,24 +254,20 @@ static bool isSupportedType(mlir::Type ty, const 
DataLayout &dl) {
     // x86_64 has no calling convention for one.
     if (vecTy.getIsScalable())
       return false;
-    // The classifier sizes a vector as element count times element width, so
-    // an element is only usable where that width is the one clang gives it.
-    // It is not for bool (a bit to clang, a byte here), for a _BitInt narrower
-    // than a byte (clang rounds to the storage container), or for x87 long
-    // double (80 bits here against clang's 128).
+    // The classifier and clang have to agree on the vector's size, which they
+    // do not for a bool element (a bit to clang, a byte to the classifier).
+    // A _BitInt element narrower than a byte has no memory representation in
+    // CIR's lowering to LLVM.
     mlir::Type elemTy = vecTy.getElementType();
     if (auto elemInt = dyn_cast<cir::IntType>(elemTy)) {
       if (elemInt.getWidth() % 8)
         return false;
-    } else if (auto elemFp = dyn_cast<cir::FPTypeInterface>(elemTy)) {
-      if (&elemFp.getFloatSemantics() == &llvm::APFloat::x87DoubleExtended())
-        return false;
-    } else {
+    } else if (!isa<cir::FPTypeInterface>(elemTy)) {
       return false;
     }
-    // Clang also rounds the vector's own width up to a power of two, and the
-    // classifier branches on the exact width, so a three-char vector would be
-    // classified at 24 bits where clang uses 32.
+    // The classifier rounds the vector's own width up to a power of two, as
+    // clang does, and the CIR data layout does not, so a three-char vector
+    // would get a 32-bit coercion for a value CIR gives 24 bits.
     if (!llvm::isPowerOf2_64(dl.getTypeSizeInBits(ty).getFixedValue()))
       return false;
     return isSupportedType(elemTy, dl);
@@ -261,6 +306,26 @@ static bool isSupportedType(mlir::Type ty, const 
DataLayout &dl) {
       // there is no member here to build the Indirect coercion from.
       if (members.empty() && recordBits > 128)
         return false;
+      // A union is moved as a value of its storage type, and an x87 value
+      // stores fewer bytes than it occupies, so the bytes another member
+      // holds past them would be lost.  A long double stores its first 10
+      // bytes, so it can share a union with other long double members and
+      // with members whose data ends within those bytes, provided it is the
+      // storage type.  An x87 value held any other way, in a _Complex, a
+      // vector, a record or an array, is measured at its full storage, which
+      // reaches past them.
+      if (members.size() > 1 && llvm::any_of(members, holdsX87LongDouble)) {
+        uint64_t x87StoreBytes = llvm::APFloat::semanticsSizeInBits(
+                                     llvm::APFloat::x87DoubleExtended()) /
+                                 8;
+        if (!isX87LongDouble(
+                mlir::cast<cir::UnionType>(recTy).getUnionStorageType(dl)) ||
+            !llvm::all_of(members, [&](mlir::Type m) {
+              return isX87LongDouble(m) ||
+                     getDataExtentInBytes(m, dl) <= x87StoreBytes;
+            }))
+          return false;
+      }
       // A `_BitInt` access unit past an eightbyte has byte-array storage in
       // CIR and integer storage in classic CodeGen, so the narrowing walk
       // finds an i8 in the second eightbyte where classic keeps an i64.
@@ -342,6 +407,19 @@ static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, 
MLIRContext *ctx) {
       .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
 }
 
+/// Returns \p ty with a LongDoubleType vector element replaced by its
+/// underlying format, which is the element abiTypeToCIR builds for the same
+/// float.
+static mlir::Type unwrapLongDoubleVectorElement(mlir::Type ty) {
+  if (auto vecTy = dyn_cast<cir::VectorType>(ty)) {
+    if (auto longDoubleTy =
+            dyn_cast<cir::LongDoubleType>(vecTy.getElementType()))
+      return cir::VectorType::get(longDoubleTy.getUnderlying(), 
vecTy.getSize(),
+                                  vecTy.getIsScalable());
+  }
+  return ty;
+}
+
 /// Map a CIR type to an llvm::abi::Type.  classifyX86_64Function pre-filters
 /// the signature, so only the scalar and struct/array types handled here can
 /// reach this function.
@@ -605,13 +683,14 @@ convertABIArgInfo(const llvm::abi::ArgInfo &info, 
MLIRContext *ctx,
     mlir::Type coerced = abiTypeToCIR(coerceAbi, ctx);
     if (!coerced)
       return std::nullopt;
+    bool coerceIsNatural = coerced == unwrapLongDoubleVectorElement(origTy);
     // An offset would be lost here, since a coercion to the value's own type
     // is indistinguishable from no coercion at all.
-    if (offset && coerced == origTy)
+    if (offset && coerceIsNatural)
       return std::nullopt;
     // Coercing a value to the type it already has would add a memory round
     // trip for nothing.
-    if (comparesAgainstCoerce && coerced == origTy)
+    if (comparesAgainstCoerce && coerceIsNatural)
       return ArgClassification::getDirect();
     ArgClassification classified =
         ArgClassification::getDirect(coerced, offset);
diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c 
b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
new file mode 100644
index 00000000000000..dc1623f2df4d72
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
@@ -0,0 +1,291 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefixes=CIR,CIR-SSE --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-SSE --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-SSE --input-file=%t.ll %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx -fclangir -emit-cir %s -o %t-avx.cir
+// RUN: FileCheck --check-prefixes=CIR,CIR-AVX --input-file=%t-avx.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx -fclangir -emit-llvm %s -o %t-avx-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-AVX --input-file=%t-avx-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx -emit-llvm %s -o %t-avx.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-AVX --input-file=%t-avx.ll %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx512f -fclangir -emit-cir %s -o %t-avx512.cir
+// RUN: FileCheck --check-prefixes=CIR,CIR-AVX512 --input-file=%t-avx512.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx512f -fclangir -emit-llvm %s -o %t-avx512-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-AVX512 
--input-file=%t-avx512-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-psabi -target-feature 
+avx512f -emit-llvm %s -o %t-avx512.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-AVX512 --input-file=%t-avx512.ll 
%s
+
+typedef long double ld1 __attribute__((vector_size(16)));
+typedef long double ld2 __attribute__((vector_size(32)));
+typedef long double ld4 __attribute__((vector_size(64)));
+
+// A one-element x87 vector occupies 16 bytes and passes in a register as
+// itself, so the value keeps its long double element and is not coerced.
+ld1 take_ld1(ld1 v) { return v; }
+
+// CIR: cir.func {{.*}}@take_ld1(%arg0: !cir.vector<1 x 
!cir.long_double<!cir.f80>> {llvm.noundef} loc({{[^)]+}})) -> !cir.vector<1 x 
!cir.long_double<!cir.f80>>
+// CIR: cir.store %arg0, %{{.+}} : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.ptr<!cir.vector<1 x 
!cir.long_double<!cir.f80>>>
+// LLVM: define dso_local <1 x x86_fp80> @take_ld1(<1 x x86_fp80> noundef 
%{{[^,)]+}})
+
+ld1 call_ld1(ld1 v) { return take_ld1(v); }
+
+// CIR: cir.call @take_ld1(%{{.+}}) : (!cir.vector<1 x 
!cir.long_double<!cir.f80>> {llvm.noundef}) -> !cir.vector<1 x 
!cir.long_double<!cir.f80>>
+// LLVM: call <1 x x86_fp80> @take_ld1(<1 x x86_fp80> noundef %{{[^,)]+}})
+
+// A record holding only that vector, directly, in a one-element array, as a
+// union member, or inside another record, is coerced to a vector of the
+// underlying x87 format.
+struct S1 { ld1 v; };
+struct S1 take_s1(struct S1 s) { return s; }
+
+// CIR: cir.func {{.*}}@take_s1(%arg0: !cir.vector<1 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<1 x !cir.f80>
+// LLVM: define dso_local <1 x x86_fp80> @take_s1(<1 x x86_fp80> %{{[^,)]+}})
+
+struct S1 call_s1(struct S1 s) { return take_s1(s); }
+
+// CIR: cir.call @take_s1(%{{.+}}) : (!cir.vector<1 x !cir.f80>) -> 
!cir.vector<1 x !cir.f80>
+// LLVM: call <1 x x86_fp80> @take_s1(<1 x x86_fp80> %{{[^,)]+}})
+
+struct A1 { ld1 a[1]; };
+struct A1 take_a1(struct A1 a) { return a; }
+
+// CIR: cir.func {{.*}}@take_a1(%arg0: !cir.vector<1 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<1 x !cir.f80>
+// LLVM: define dso_local <1 x x86_fp80> @take_a1(<1 x x86_fp80> %{{[^,)]+}})
+
+union U1 { ld1 v; };
+union U1 take_u1(union U1 u) { return u; }
+
+// CIR: cir.func {{.*}}@take_u1(%arg0: !cir.vector<1 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<1 x !cir.f80>
+// LLVM: define dso_local <1 x x86_fp80> @take_u1(<1 x x86_fp80> %{{[^,)]+}})
+
+struct N1 { struct S1 s; };
+struct N1 take_n1(struct N1 n) { return n; }
+
+// CIR: cir.func {{.*}}@take_n1(%arg0: !cir.vector<1 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<1 x !cir.f80>
+// LLVM: define dso_local <1 x x86_fp80> @take_n1(<1 x x86_fp80> %{{[^,)]+}})
+
+// A struct holding anything past the one vector is larger than 16 bytes, so
+// it passes in memory.
+struct P2 { ld1 a; ld1 b; };
+struct P2 take_p2(struct P2 p) { return p; }
+
+// CIR: cir.func {{.*}}@take_p2(%arg0: !cir.ptr<!rec_P2> {llvm.align = 16 : 
i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_P2, llvm.writable} 
loc({{[^)]+}}), %arg1: !cir.ptr<!rec_P2> {llvm.align = 16 : i64, llvm.byval = 
!rec_P2, llvm.noundef} loc({{[^)]+}}))
+// LLVM: define dso_local void @take_p2(ptr dead_on_unwind noalias writable 
sret(%struct.P2) align 16 %{{[^,)]+}}, ptr noundef byval(%struct.P2) align 16 
%{{[^,)]+}})
+
+struct M1 { int i; ld1 v; };
+struct M1 take_m1(struct M1 m) { return m; }
+
+// CIR: cir.func {{.*}}@take_m1(%arg0: !cir.ptr<!rec_M1> {llvm.align = 16 : 
i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_M1, llvm.writable} 
loc({{[^)]+}}), %arg1: !cir.ptr<!rec_M1> {llvm.align = 16 : i64, llvm.byval = 
!rec_M1, llvm.noundef} loc({{[^)]+}}))
+// LLVM: define dso_local void @take_m1(ptr dead_on_unwind noalias writable 
sret(%struct.M1) align 16 %{{[^,)]+}}, ptr noundef byval(%struct.M1) align 16 
%{{[^,)]+}})
+
+// An unnamed argument up to 16 bytes is passed the same way as a named one.
+void var(int n, ...);
+void call_var(struct S1 s) { var(1, s); }
+
+// CIR: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, !cir.vector<1 
x !cir.f80>) -> ()
+// LLVM: call void (i32, ...) @var(i32 noundef 1, <1 x x86_fp80> %{{[^,)]+}})
+
+void var_ld1(ld1 v) { var(1, v); }
+
+// CIR: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, !cir.vector<1 
x !cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM: call void (i32, ...) @var(i32 noundef 1, <1 x x86_fp80> noundef 
%{{[^,)]+}})
+
+// A wider vector takes the same IR form unnamed as named: it classifies as
+// memory when unnamed, but a vector is still passed directly.
+void var_ld2(ld2 v) { var(1, v); }
+
+// CIR-SSE: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.ptr<!cir.vector<2 x !cir.long_double<!cir.f80>>> {llvm.align = 32 : i64, 
llvm.byval = !cir.vector<2 x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.vector<2 x !cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.vector<2 x !cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM-SSE: call void (i32, ...) @var(i32 noundef 1, ptr noundef byval(<2 x 
x86_fp80>) align 32 %{{[^,)]+}})
+// LLVM-AVX: call void (i32, ...) @var(i32 noundef 1, <2 x x86_fp80> noundef 
%{{[^,)]+}})
+// LLVM-AVX512: call void (i32, ...) @var(i32 noundef 1, <2 x x86_fp80> 
noundef %{{[^,)]+}})
+
+// va_arg takes a one-element vector from an SSE register slot, 16 bytes at a
+// time, or from the overflow area aligned to 16.
+ld1 arg_ld1(int n, ...) {
+  __builtin_va_list ap;
+  __builtin_va_start(ap, n);
+  ld1 v = __builtin_va_arg(ap, ld1);
+  __builtin_va_end(ap);
+  return v;
+}
+
+// CIR-LABEL: cir.func {{.*}}@arg_ld1(
+// CIR: %[[FP_OFFSET_P:.+]] = cir.get_member %{{.+}}[1] {name = "fp_offset"}
+// CIR: %[[FP_OFFSET:.+]] = cir.load %[[FP_OFFSET_P]]
+// CIR: %[[FP_LIMIT:.+]] = cir.const #cir.int<160> : !u32i
+// CIR: cir.cmp le %[[FP_OFFSET]], %[[FP_LIMIT]] : !u32i
+// CIR: %[[FP_STEP:.+]] = cir.const #cir.int<16> : !u32i
+// CIR: cir.add %[[FP_OFFSET]], %[[FP_STEP]] : !u32i
+// CIR: %[[ALIGN_MASK:.+]] = cir.const #cir.int<-16> : !s64i
+// CIR: %[[ALIGNED:.+]] = cir.ptr_mask %{{.+}}, %[[ALIGN_MASK]]
+// CIR: %[[MEM_STEP:.+]] = cir.const #cir.int<16> : !s32i
+// CIR: cir.ptr_stride %[[ALIGNED]], %[[MEM_STEP]]
+// CIR: %[[VPTR:.+]] = cir.cast bitcast %{{.+}} : !cir.ptr<!u8i> -> 
!cir.ptr<!cir.vector<1 x !cir.long_double<!cir.f80>>>
+// CIR: cir.load align(16) %[[VPTR]] : !cir.ptr<!cir.vector<1 x 
!cir.long_double<!cir.f80>>>, !cir.vector<1 x !cir.long_double<!cir.f80>>
+
+// LLVM-LABEL: define dso_local <1 x x86_fp80> @arg_ld1(i32 noundef 
%{{[^,)]+}}, ...)
+// LLVM: icmp ule i32 %{{.+}}, 160
+// LLVM: add i32 %{{.+}}, 16
+// LLVM: call ptr @llvm.ptrmask.p0.i64(ptr %{{.+}}, i64 -16)
+// LLVM: %[[ADDR:.+]] = phi ptr
+// LLVM: load <1 x x86_fp80>, ptr %[[ADDR]], align 16
+
+// A wider vector is always taken from the overflow area, aligned to its size.
+ld2 arg_ld2(int n, ...) {
+  __builtin_va_list ap;
+  __builtin_va_start(ap, n);
+  ld2 v = __builtin_va_arg(ap, ld2);
+  __builtin_va_end(ap);
+  return v;
+}
+
+// CIR-LABEL: cir.func {{.*}}@arg_ld2(
+// CIR-NOT: fp_offset
+// CIR: %[[ALIGN_MASK:.+]] = cir.const #cir.int<-32> : !s64i
+// CIR: %[[ALIGNED:.+]] = cir.ptr_mask %{{.+}}, %[[ALIGN_MASK]]
+// CIR: %[[MEM_STEP:.+]] = cir.const #cir.int<32> : !s32i
+// CIR: cir.ptr_stride %[[ALIGNED]], %[[MEM_STEP]]
+// CIR: %[[VPTR:.+]] = cir.cast bitcast %[[ALIGNED]] : !cir.ptr<!u8i> -> 
!cir.ptr<!cir.vector<2 x !cir.long_double<!cir.f80>>>
+// CIR: cir.load align(32) %[[VPTR]] : !cir.ptr<!cir.vector<2 x 
!cir.long_double<!cir.f80>>>, !cir.vector<2 x !cir.long_double<!cir.f80>>
+
+// LLVM-LABEL: define dso_local <2 x x86_fp80> @arg_ld2(i32 noundef 
%{{[^,)]+}}, ...)
+// LLVM-NOT: icmp
+// LLVM: %[[ALIGNED:.+]] = call ptr @llvm.ptrmask.p0.i64(ptr %{{.+}}, i64 -32)
+// LLVM: load <2 x x86_fp80>, ptr %[[ALIGNED]], align 32
+
+// Two elements take 32 bytes, which pass in memory below AVX.  The vector on
+// its own is still returned directly, but a record holding it is returned
+// through sret.
+void take_ld2(ld2 v) {}
+
+// CIR-SSE: cir.func {{.*}}@take_ld2(%arg0: !cir.ptr<!cir.vector<2 x 
!cir.long_double<!cir.f80>>> {llvm.align = 32 : i64, llvm.byval = !cir.vector<2 
x !cir.long_double<!cir.f80>>, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@take_ld2(%arg0: !cir.vector<2 x 
!cir.long_double<!cir.f80>> {llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX512: cir.func {{.*}}@take_ld2(%arg0: !cir.vector<2 x 
!cir.long_double<!cir.f80>> {llvm.noundef} loc({{[^)]+}}))
+// LLVM-SSE: define dso_local void @take_ld2(ptr noundef byval(<2 x x86_fp80>) 
align 32 %{{[^,)]+}})
+// LLVM-AVX: define dso_local void @take_ld2(<2 x x86_fp80> noundef 
%{{[^,)]+}})
+// LLVM-AVX512: define dso_local void @take_ld2(<2 x x86_fp80> noundef 
%{{[^,)]+}})
+
+void call_ld2(ld2 v) { take_ld2(v); }
+
+// CIR-SSE: cir.call @take_ld2(%{{.+}}) : (!cir.ptr<!cir.vector<2 x 
!cir.long_double<!cir.f80>>> {llvm.align = 32 : i64, llvm.byval = !cir.vector<2 
x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @take_ld2(%{{.+}}) : (!cir.vector<2 x 
!cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @take_ld2(%{{.+}}) : (!cir.vector<2 x 
!cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM-SSE: call void @take_ld2(ptr noundef byval(<2 x x86_fp80>) align 32 
%{{[^,)]+}})
+// LLVM-AVX: call void @take_ld2(<2 x x86_fp80> noundef %{{[^,)]+}})
+// LLVM-AVX512: call void @take_ld2(<2 x x86_fp80> noundef %{{[^,)]+}})
+
+ld2 ret_ld2(ld2 *p) { return *p; }
+
+// CIR: cir.func {{.*}}@ret_ld2(%arg0: !cir.ptr<!cir.vector<2 x 
!cir.long_double<!cir.f80>>> {llvm.noundef} loc({{[^)]+}})) -> !cir.vector<2 x 
!cir.long_double<!cir.f80>>
+// LLVM: define dso_local <2 x x86_fp80> @ret_ld2(ptr noundef %{{[^,)]+}})
+
+ld2 call_ret_ld2(ld2 *p) { return ret_ld2(p); }
+
+// CIR: cir.call @ret_ld2(%{{.+}}) : (!cir.ptr<!cir.vector<2 x 
!cir.long_double<!cir.f80>>> {llvm.noundef}) -> !cir.vector<2 x 
!cir.long_double<!cir.f80>>
+// LLVM: call <2 x x86_fp80> @ret_ld2(ptr noundef %{{[^,)]+}})
+
+struct S2 { ld2 v; };
+struct S2 take_s2(struct S2 s) { return s; }
+
+// CIR-SSE: cir.func {{.*}}@take_s2(%arg0: !cir.ptr<!rec_S2> {llvm.align = 32 
: i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_S2, llvm.writable} 
loc({{[^)]+}}), %arg1: !cir.ptr<!rec_S2> {llvm.align = 32 : i64, llvm.byval = 
!rec_S2, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@take_s2(%arg0: !cir.vector<2 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<2 x !cir.f80>
+// CIR-AVX512: cir.func {{.*}}@take_s2(%arg0: !cir.vector<2 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<2 x !cir.f80>
+// LLVM-SSE: define dso_local void @take_s2(ptr dead_on_unwind noalias 
writable sret(%struct.S2) align 32 %{{[^,)]+}}, ptr noundef byval(%struct.S2) 
align 32 %{{[^,)]+}})
+// LLVM-AVX: define dso_local <2 x x86_fp80> @take_s2(<2 x x86_fp80> 
%{{[^,)]+}})
+// LLVM-AVX512: define dso_local <2 x x86_fp80> @take_s2(<2 x x86_fp80> 
%{{[^,)]+}})
+
+struct S2 call_s2(struct S2 s) { return take_s2(s); }
+
+// CIR-SSE: cir.call @take_s2(%arg0, %{{.+}}) : (!cir.ptr<!rec_S2> {llvm.align 
= 32 : i64, llvm.dead_on_unwind, llvm.sret = !rec_S2, llvm.writable}, 
!cir.ptr<!rec_S2> {llvm.align = 32 : i64, llvm.byval = !rec_S2, llvm.noundef}) 
-> ()
+// CIR-AVX: cir.call @take_s2(%{{.+}}) : (!cir.vector<2 x !cir.f80>) -> 
!cir.vector<2 x !cir.f80>
+// CIR-AVX512: cir.call @take_s2(%{{.+}}) : (!cir.vector<2 x !cir.f80>) -> 
!cir.vector<2 x !cir.f80>
+// LLVM-SSE: call void @take_s2(ptr dead_on_unwind writable sret(%struct.S2) 
align 32 %{{[^,)]+}}, ptr noundef byval(%struct.S2) align 32 %{{[^,)]+}})
+// LLVM-AVX: call <2 x x86_fp80> @take_s2(<2 x x86_fp80> %{{[^,)]+}})
+// LLVM-AVX512: call <2 x x86_fp80> @take_s2(<2 x x86_fp80> %{{[^,)]+}})
+
+// Unnamed, a record wider than 16 bytes goes to memory even where a named one
+// is passed in registers.
+void var_s2(struct S2 s) { var(1, s); }
+
+// CIR-SSE: cir.func {{.*}}@var_s2(%arg0: !cir.ptr<!rec_S2> {llvm.align = 32 : 
i64, llvm.byval = !rec_S2, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@var_s2(%arg0: !cir.vector<2 x !cir.f80> 
loc({{[^)]+}}))
+// CIR-AVX512: cir.func {{.*}}@var_s2(%arg0: !cir.vector<2 x !cir.f80> 
loc({{[^)]+}}))
+// CIR: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.ptr<!rec_S2> {llvm.align = 32 : i64, llvm.byval = !rec_S2, llvm.noundef}) 
-> ()
+// LLVM-SSE: define dso_local void @var_s2(ptr noundef byval(%struct.S2) align 
32 %{{[^,)]+}})
+// LLVM-AVX: define dso_local void @var_s2(<2 x x86_fp80> %{{[^,)]+}})
+// LLVM-AVX512: define dso_local void @var_s2(<2 x x86_fp80> %{{[^,)]+}})
+// LLVM: call void (i32, ...) @var(i32 noundef 1, ptr noundef 
byval(%struct.S2) align 32 %{{[^,)]+}})
+
+// Four elements take 64 bytes, which pass in registers only with AVX-512.
+void take_ld4(ld4 v) {}
+
+// CIR-SSE: cir.func {{.*}}@take_ld4(%arg0: !cir.ptr<!cir.vector<4 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<4 
x !cir.long_double<!cir.f80>>, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@take_ld4(%arg0: !cir.ptr<!cir.vector<4 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<4 
x !cir.long_double<!cir.f80>>, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX512: cir.func {{.*}}@take_ld4(%arg0: !cir.vector<4 x 
!cir.long_double<!cir.f80>> {llvm.noundef} loc({{[^)]+}}))
+// LLVM-SSE: define dso_local void @take_ld4(ptr noundef byval(<4 x x86_fp80>) 
align 64 %{{[^,)]+}})
+// LLVM-AVX: define dso_local void @take_ld4(ptr noundef byval(<4 x x86_fp80>) 
align 64 %{{[^,)]+}})
+// LLVM-AVX512: define dso_local void @take_ld4(<4 x x86_fp80> noundef 
%{{[^,)]+}})
+
+void call_ld4(ld4 v) { take_ld4(v); }
+
+// CIR-SSE: cir.call @take_ld4(%{{.+}}) : (!cir.ptr<!cir.vector<4 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<4 
x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @take_ld4(%{{.+}}) : (!cir.ptr<!cir.vector<4 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<4 
x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @take_ld4(%{{.+}}) : (!cir.vector<4 x 
!cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM-SSE: call void @take_ld4(ptr noundef byval(<4 x x86_fp80>) align 64 
%{{[^,)]+}})
+// LLVM-AVX: call void @take_ld4(ptr noundef byval(<4 x x86_fp80>) align 64 
%{{[^,)]+}})
+// LLVM-AVX512: call void @take_ld4(<4 x x86_fp80> noundef %{{[^,)]+}})
+
+struct S4 { ld4 v; };
+struct S4 take_s4(struct S4 s) { return s; }
+
+// CIR-SSE: cir.func {{.*}}@take_s4(%arg0: !cir.ptr<!rec_S4> {llvm.align = 64 
: i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_S4, llvm.writable} 
loc({{[^)]+}}), %arg1: !cir.ptr<!rec_S4> {llvm.align = 64 : i64, llvm.byval = 
!rec_S4, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@take_s4(%arg0: !cir.ptr<!rec_S4> {llvm.align = 64 
: i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_S4, llvm.writable} 
loc({{[^)]+}}), %arg1: !cir.ptr<!rec_S4> {llvm.align = 64 : i64, llvm.byval = 
!rec_S4, llvm.noundef} loc({{[^)]+}}))
+// CIR-AVX512: cir.func {{.*}}@take_s4(%arg0: !cir.vector<4 x !cir.f80> 
loc({{[^)]+}})) -> !cir.vector<4 x !cir.f80>
+// LLVM-SSE: define dso_local void @take_s4(ptr dead_on_unwind noalias 
writable sret(%struct.S4) align 64 %{{[^,)]+}}, ptr noundef byval(%struct.S4) 
align 64 %{{[^,)]+}})
+// LLVM-AVX: define dso_local void @take_s4(ptr dead_on_unwind noalias 
writable sret(%struct.S4) align 64 %{{[^,)]+}}, ptr noundef byval(%struct.S4) 
align 64 %{{[^,)]+}})
+// LLVM-AVX512: define dso_local <4 x x86_fp80> @take_s4(<4 x x86_fp80> 
%{{[^,)]+}})
+
+struct S4 call_s4(struct S4 s) { return take_s4(s); }
+
+// CIR-SSE: cir.call @take_s4(%arg0, %{{.+}}) : (!cir.ptr<!rec_S4> {llvm.align 
= 64 : i64, llvm.dead_on_unwind, llvm.sret = !rec_S4, llvm.writable}, 
!cir.ptr<!rec_S4> {llvm.align = 64 : i64, llvm.byval = !rec_S4, llvm.noundef}) 
-> ()
+// CIR-AVX: cir.call @take_s4(%arg0, %{{.+}}) : (!cir.ptr<!rec_S4> {llvm.align 
= 64 : i64, llvm.dead_on_unwind, llvm.sret = !rec_S4, llvm.writable}, 
!cir.ptr<!rec_S4> {llvm.align = 64 : i64, llvm.byval = !rec_S4, llvm.noundef}) 
-> ()
+// CIR-AVX512: cir.call @take_s4(%{{.+}}) : (!cir.vector<4 x !cir.f80>) -> 
!cir.vector<4 x !cir.f80>
+// LLVM-SSE: call void @take_s4(ptr dead_on_unwind writable sret(%struct.S4) 
align 64 %{{[^,)]+}}, ptr noundef byval(%struct.S4) align 64 %{{[^,)]+}})
+// LLVM-AVX: call void @take_s4(ptr dead_on_unwind writable sret(%struct.S4) 
align 64 %{{[^,)]+}}, ptr noundef byval(%struct.S4) align 64 %{{[^,)]+}})
+// LLVM-AVX512: call <4 x x86_fp80> @take_s4(<4 x x86_fp80> %{{[^,)]+}})
+
+// A long double stores its first 10 bytes, so it can share a union with
+// members whose data ends within them, counting neither a record's tail
+// padding nor an empty array.
+struct __attribute__((aligned(16))) C16 { char c; };
+union LDPad { long double ld; struct C16 s; };
+union LDPad take_ldpad(union LDPad u) { return u; }
+
+// CIR: cir.func {{.*}}@take_ldpad(%arg0: !cir.ptr<!rec_LDPad> {llvm.align = 
16 : i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_LDPad, 
llvm.writable} loc({{[^)]+}}), %arg1: !cir.ptr<!rec_LDPad> {llvm.align = 16 : 
i64, llvm.byval = !rec_LDPad, llvm.noundef} loc({{[^)]+}}))
+// LLVM: define dso_local void @take_ldpad(ptr dead_on_unwind noalias writable 
sret(%union.LDPad) align 16 %{{[^,)]+}}, ptr noundef byval(%union.LDPad) align 
16 %{{[^,)]+}})
+
+union LDZero { long double ld; int z[0]; };
+union LDZero take_ldzero(union LDZero u) { return u; }
+
+// CIR: cir.func {{.*}}@take_ldzero(%arg0: !cir.ptr<!rec_LDZero> {llvm.align = 
16 : i64, llvm.byval = !rec_LDZero, llvm.noundef} loc({{[^)]+}})) -> !cir.f80
+// LLVM: define dso_local x86_fp80 @take_ldzero(ptr noundef 
byval(%union.LDZero) align 16 %{{[^,)]+}})
+
+// An empty array of long double holds no x87 value, so a union holding one is
+// classified like any other.
+union EmptyX87 { long l; long double z[0]; };
+union EmptyX87 take_empty_x87(union EmptyX87 u) { return u; }
+
+// CIR: cir.func {{.*}}@take_empty_x87(%arg0: !s64i loc({{[^)]+}})) -> !s64i
+// LLVM: define dso_local i64 @take_empty_x87(i64 %{{[^,)]+}})
+
+union LDTen { long double ld; char c[10]; };
+union LDTen take_ldten(union LDTen u) { return u; }
+
+// CIR: cir.func {{.*}}@take_ldten(%arg0: !u64i loc({{[^)]+}}), %arg1: !u64i 
loc({{[^)]+}})) -> !rec_anon_struct{{[0-9]*}}
+// LLVM: define dso_local { i64, i64 } @take_ldten(i64 %{{[^,)]+}}, i64 
%{{[^,)]+}})
diff --git a/clang/test/CIR/CodeGen/vector-logical-long-double.cpp 
b/clang/test/CIR/CodeGen/vector-logical-long-double.cpp
new file mode 100644
index 00000000000000..ac580a1fd76402
--- /dev/null
+++ b/clang/test/CIR/CodeGen/vector-logical-long-double.cpp
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVMCIR --implicit-check-not=i80 
--input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --implicit-check-not=i80 
--input-file=%t.ll %s
+
+typedef long double ld1 __attribute__((vector_size(16)));
+typedef __int128 m1 __attribute__((vector_size(16)));
+typedef long double ld2 __attribute__((vector_size(32)));
+typedef __int128 m2 __attribute__((vector_size(32)));
+
+// !, && and || on an x87 vector yield 128-bit integer elements.
+void lnot(m1 *r, ld1 *v) { *r = !*v; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z4lnotPDv1_nPDv1_e(
+// CIR: cir.vec.cmp(eq, %{{.+}}, %{{.+}}) : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.vector<1 x !s128i>
+
+// LLVM-LABEL: define dso_local void @_Z4lnotPDv1_nPDv1_e(ptr noundef 
%{{[^,)]+}}, ptr noundef %{{[^,)]+}})
+// LLVM: %[[CMP:.+]] = fcmp oeq <1 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVM: %[[EXT:.+]] = sext <1 x i1> %[[CMP]] to <1 x i128>
+// LLVM: store <1 x i128> %[[EXT]], ptr %{{.+}}, align 16
+
+void lnot2(m2 *r, ld2 *v) { *r = !*v; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z5lnot2PDv2_nPDv2_e(
+// CIR: cir.vec.cmp(eq, %{{.+}}, %{{.+}}) : !cir.vector<2 x 
!cir.long_double<!cir.f80>>, !cir.vector<2 x !s128i>
+
+// LLVM-LABEL: define dso_local void @_Z5lnot2PDv2_nPDv2_e(ptr noundef 
%{{[^,)]+}}, ptr noundef %{{[^,)]+}})
+// LLVM: %[[CMP:.+]] = fcmp oeq <2 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVM: %[[EXT:.+]] = sext <2 x i1> %[[CMP]] to <2 x i128>
+// LLVM: store <2 x i128> %[[EXT]], ptr %{{.+}}, align 32
+
+void land(m1 *r, ld1 *a, ld1 *b) { *r = *a && *b; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z4landPDv1_nPDv1_eS2_(
+// CIR: %[[A:.+]] = cir.vec.cmp(ne, %{{.+}}, %{{.+}}) : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.vector<1 x !s128i>
+// CIR: %[[B:.+]] = cir.vec.cmp(ne, %{{.+}}, %{{.+}}) : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.vector<1 x !s128i>
+// CIR: cir.and %[[A]], %[[B]] : !cir.vector<1 x !s128i>
+
+// LLVM-LABEL: define dso_local void @_Z4landPDv1_nPDv1_eS2_(ptr noundef 
%{{[^,)]+}}, ptr noundef %{{[^,)]+}}, ptr noundef %{{[^,)]+}})
+// LLVM: %[[A:.+]] = fcmp une <1 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVMCIR: %[[A_EXT:.+]] = sext <1 x i1> %[[A]] to <1 x i128>
+// LLVM: %[[B:.+]] = fcmp une <1 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVMCIR: %[[B_EXT:.+]] = sext <1 x i1> %[[B]] to <1 x i128>
+// LLVMCIR: %[[RES:.+]] = and <1 x i128> %[[A_EXT]], %[[B_EXT]]
+// OGCG: %[[AND:.+]] = and <1 x i1> %[[A]], %[[B]]
+// OGCG: %[[RES:.+]] = sext <1 x i1> %[[AND]] to <1 x i128>
+// LLVM: store <1 x i128> %[[RES]], ptr %{{.+}}, align 16
+
+void lor(m1 *r, ld1 *a, ld1 *b) { *r = *a || *b; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z3lorPDv1_nPDv1_eS2_(
+// CIR: %[[A:.+]] = cir.vec.cmp(ne, %{{.+}}, %{{.+}}) : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.vector<1 x !s128i>
+// CIR: %[[B:.+]] = cir.vec.cmp(ne, %{{.+}}, %{{.+}}) : !cir.vector<1 x 
!cir.long_double<!cir.f80>>, !cir.vector<1 x !s128i>
+// CIR: cir.or %[[A]], %[[B]] : !cir.vector<1 x !s128i>
+
+// LLVM-LABEL: define dso_local void @_Z3lorPDv1_nPDv1_eS2_(ptr noundef 
%{{[^,)]+}}, ptr noundef %{{[^,)]+}}, ptr noundef %{{[^,)]+}})
+// LLVM: %[[A:.+]] = fcmp une <1 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVMCIR: %[[A_EXT:.+]] = sext <1 x i1> %[[A]] to <1 x i128>
+// LLVM: %[[B:.+]] = fcmp une <1 x x86_fp80> %{{.+}}, zeroinitializer
+// LLVMCIR: %[[B_EXT:.+]] = sext <1 x i1> %[[B]] to <1 x i128>
+// LLVMCIR: %[[RES:.+]] = or <1 x i128> %[[A_EXT]], %[[B_EXT]]
+// OGCG: %[[OR:.+]] = or <1 x i1> %[[A]], %[[B]]
+// OGCG: %[[RES:.+]] = sext <1 x i1> %[[OR]] to <1 x i128>
+// LLVM: store <1 x i128> %[[RES]], ptr %{{.+}}, align 16
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
index 59a2583b797e74..1003172ebd9c61 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
@@ -12,6 +12,35 @@
                                                      
[#cir.bitfield_decl<!cir.int<s, 72, bitint>, 70>]>},
                padding = {!cir.array<!u8i x 7>}>
 !rec_SVec = !cir.struct<"SVec" {data !cir.vector<4 x !cir.float>}>
+!rec_SX87 = !cir.struct<"SX87" {data !cir.vector<1 x 
!cir.long_double<!cir.f80>>}>
+!rec_UX87Bytes =
+    !cir.union<"UX87Bytes" {data !cir.vector<1 x !cir.long_double<!cir.f80>>,
+                            data !cir.array<!u8i x 16>}>
+!rec_UX87InRec =
+    !cir.union<"UX87InRec" {data !rec_SX87, data !cir.array<!u8i x 16>}>
+!rec_UX87InArr =
+    !cir.union<"UX87InArr" {data !cir.array<!cir.vector<1 x 
!cir.long_double<!cir.f80>> x 1>,
+                            data !cir.array<!u8i x 16>}>
+!rec_SUX87 = !cir.struct<"SUX87" {data !rec_UX87Bytes}>
+!rec_UX87Twice =
+    !cir.union<"UX87Twice" {data !cir.vector<1 x !cir.long_double<!cir.f80>>,
+                            data !cir.vector<1 x !cir.long_double<!cir.f80>>}>
+!rec_UX87Scalar =
+    !cir.union<"UX87Scalar" {data !cir.long_double<!cir.f80>,
+                             data !cir.array<!u8i x 11>}>
+!rec_SLongDouble = !cir.struct<"SLongDouble" {data !cir.long_double<!cir.f80>}>
+!rec_UX87Nested =
+    !cir.union<"UX87Nested" {data !rec_SLongDouble, data !cir.array<!u8i x 4>}>
+!rec_UEmptyX87 =
+    !cir.union<"UEmptyX87" {data !s32i,
+                            empty !cir.array<!cir.long_double<!cir.f80> x 0>},
+               padding = {!cir.array<!u8i x 16>}>
+!rec_UX87Displaced =
+    !cir.union<"UX87Displaced" {data !rec_UEmptyX87,
+                                data !cir.long_double<!cir.f80>}>
+!rec_UX87Complex =
+    !cir.union<"UX87Complex" {data !cir.complex<!cir.long_double<!cir.f80>>,
+                              data !cir.array<!u8i x 32>}>
 !rec_UVec384 =
     !cir.union<"UVec384" {data !cir.vector<4 x !cir.float>, data 
!cir.array<!s8i x 33>},
                padding = {!cir.array<!u8i x 32>}>
@@ -148,33 +177,94 @@ module attributes {
 
   // CHECK: not yet implemented for type '!cir.vector<[4] x !cir.float>
 
-  // Clang pads a vector's type width up to a power of two and the classifier
-  // branches on the exact width, so a three-byte vector would be classified at
-  // 24 bits where clang uses 32.  The ABI library's vector type cannot carry
-  // the padded width, so this is rejected rather than misclassified.
+  // The classifier rounds a vector's width up to a power of two, as clang
+  // does, and the CIR data layout does not, so a three-char vector would be
+  // coerced at 32 bits from a 24-bit value.
   cir.func @take_odd_width(%arg0: !cir.vector<3 x !s8i>) {
     cir.return
   }
 
   // CHECK: not yet implemented for type '!cir.vector<3 x !cir.int<s, 8>>
 
-  // An element narrower than a byte is rounded up to its storage container by
-  // clang but not by the classifier, so the two would size the vector
-  // differently.  Classic passes this one in an xmm register from its 128-bit
-  // width, where the classifier would read 32 bits and coerce to an integer.
+  // A _BitInt element narrower than a byte has no memory representation in
+  // CIR's lowering to LLVM, so this is rejected here rather than failing
+  // there.
   cir.func @take_sub_byte_element(%arg0: !cir.vector<16 x !cir.int<s, 2, 
bitint>>) {
     cir.return
   }
 
   // CHECK: not yet implemented for type '!cir.vector<16 x !cir.int<s, 2, 
bitint>>
 
-  // An x87 long double is 80 bits to the classifier and 128 to clang, so the
-  // two would size this vector differently for the same reason.
-  cir.func @take_x87_element(%arg0: !cir.vector<2 x 
!cir.long_double<!cir.f80>>) {
+  // Three x87 elements are 384 bits, which the classifier rounds up to 512
+  // and CIR does not, so this is rejected for the same reason as the
+  // three-char vector.
+  cir.func @take_x87_three(%arg0: !cir.vector<3 x !cir.long_double<!cir.f80>>) 
{
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.vector<3 x 
!cir.long_double<!cir.f80>>
+
+  // A union is moved as a value of its storage type, so a member holding data
+  // past the 10 bytes a long double stores would lose it.  A member reaching
+  // byte 11 is refused next to a long double, and so is an x87 value held any
+  // other way, in a record, a _Complex, a vector or an array, even next to one
+  // of the same type.  A record holding such a union is refused with it.
+  cir.func @take_union_x87_scalar(%arg0: !rec_UX87Scalar) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Scalar"
+
+  cir.func @take_union_x87_nested(%arg0: !rec_UX87Nested) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Nested"
+
+  // A member that ties the long double on alignment and size, listed first,
+  // becomes the storage type in its place, so the union is refused even
+  // though that member's data ends within the 10 bytes.
+  cir.func @take_union_x87_displaced(%arg0: !rec_UX87Displaced) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Displaced"
+
+  cir.func @take_union_x87_complex(%arg0: !rec_UX87Complex) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Complex"
+
+  cir.func @take_union_x87_twice(%arg0: !rec_UX87Twice) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Twice"
+
+  cir.func @take_union_x87_bytes(%arg0: !rec_UX87Bytes) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87Bytes"
+
+  cir.func @take_union_x87_in_rec(%arg0: !rec_UX87InRec) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87InRec"
+
+  cir.func @take_union_x87_in_arr(%arg0: !rec_UX87InArr) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for type '!cir.union<"UX87InArr"
+
+  cir.func @take_struct_union_x87(%arg0: !rec_SUX87) {
     cir.return
   }
 
-  // CHECK: not yet implemented for type '!cir.vector<2 x 
!cir.long_double<!cir.f80>>
+  // CHECK: not yet implemented for type '!cir.struct<"SUX87"
 
   // A definition (unlike a declaration) is refused: no incomplete-by-value
   // definition can arise from valid C++, so this stays a hard reject rather
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir
index d5d47a8491b44d..019ae499a7418c 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir
@@ -29,6 +29,22 @@ module attributes {
   // CHECK: cir.func{{.*}} @v128(%arg0: !cir.vector<4 x !cir.float>) -> 
!cir.vector<4 x !cir.float>
   // CHECK-NEXT: cir.return %arg0
 
+  // A one-element vector of long double fills one xmm register the same way,
+  // in either underlying format, and keeps its long double element.
+  cir.func @v128_x87(%arg0: !cir.vector<1 x !cir.long_double<!cir.f80>>) -> 
!cir.vector<1 x !cir.long_double<!cir.f80>> {
+    cir.return %arg0 : !cir.vector<1 x !cir.long_double<!cir.f80>>
+  }
+
+  // CHECK: cir.func{{.*}} @v128_x87(%arg0: !cir.vector<1 x 
!cir.long_double<!cir.f80>>) -> !cir.vector<1 x !cir.long_double<!cir.f80>>
+  // CHECK-NEXT: cir.return %arg0
+
+  cir.func @v128_quad(%arg0: !cir.vector<1 x !cir.long_double<!cir.f128>>) -> 
!cir.vector<1 x !cir.long_double<!cir.f128>> {
+    cir.return %arg0 : !cir.vector<1 x !cir.long_double<!cir.f128>>
+  }
+
+  // CHECK: cir.func{{.*}} @v128_quad(%arg0: !cir.vector<1 x 
!cir.long_double<!cir.f128>>) -> !cir.vector<1 x !cir.long_double<!cir.f128>>
+  // CHECK-NEXT: cir.return %arg0
+
   // The classifier does not look inside a vector when picking the type for an
   // SSE eightbyte, so it falls back to double rather than to the two-element
   // vector that take_ff below gets for the same eightbyte.
@@ -115,6 +131,8 @@ module attributes {
 }
 
 // LLVM: define <4 x float> @v128(<4 x float> %{{[^,)]+}})
+// LLVM: define <1 x x86_fp80> @v128_x87(<1 x x86_fp80> %{{[^,)]+}})
+// LLVM: define <1 x fp128> @v128_quad(<1 x fp128> %{{[^,)]+}})
 // LLVM: define double @v64(double %{{[^,)]+}})
 // LLVM: define i32 @v32(i32 %{{[^,)]+}})
 // LLVM: define void @v256(ptr noundef byval(<8 x float>) align 32 %{{[^,)]+}})

>From 2ec9a3d68bc1cf5049984b33d4f9762968570fae Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Fri, 9 Oct 2026 09:29:00 -0700
Subject: [PATCH 2/3] Address review feedback

Assisted-by: Cursor / Claude Opus 5.5
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      | 15 ++++++---
 .../Transforms/CallConvLoweringPass.cpp       | 31 ++++++++++---------
 .../abi-lowering/x86_64-aggregate-nyi.cir     | 15 ++++-----
 3 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 36140601caab21..6dd927fabc0943 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -907,13 +907,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   }
 
   /// Compares \p lhs and \p rhs element-wise.  Each result element is a
-  /// signed integer of the operand element's bit width, 80 for x87 long
-  /// double.
+  /// signed integer of the operand element's bit width.  That is not the
+  /// result an FP80 element needs, so an FP80 compare names its result type
+  /// with the overload below.
   cir::VecCmpOp createVecCompare(mlir::Location loc, cir::CmpOpKind kind,
                                  mlir::Value lhs, mlir::Value rhs) {
     VectorType vecCast = mlir::cast<VectorType>(lhs.getType());
-    IntType integralTy =
-        getSIntNTy(getCIRIntOrFloatBitWidth(vecCast.getElementType()));
+    mlir::Type elemTy = vecCast.getElementType();
+    assert(!(mlir::isa<cir::FPTypeInterface>(elemTy) &&
+             &mlir::cast<cir::FPTypeInterface>(elemTy).getFloatSemantics() ==
+                 &llvm::APFloat::x87DoubleExtended()) &&
+           "an FP80 vector compare must name its result type");
+    IntType integralTy = getSIntNTy(getCIRIntOrFloatBitWidth(elemTy));
     return createVecCompare(loc,
                             cir::VectorType::get(integralTy, 
vecCast.getSize()),
                             kind, lhs, rhs);
@@ -922,7 +927,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   /// Compares \p lhs and \p rhs element-wise, producing \p resultTy, which
   /// must be a vector of integers or bools with as many elements as \p lhs.
   /// The result element width can then differ from the operand element's
-  /// bit width, as for x87 long double, whose result elements are 128 bits.
+  /// bit width, as for FP80, whose result elements are 128 bits.
   cir::VecCmpOp createVecCompare(mlir::Location loc, VectorType resultTy,
                                  cir::CmpOpKind kind, mlir::Value lhs,
                                  mlir::Value rhs) {
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index beeab4bf4d5e3d..15edae487df28c 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -159,26 +159,27 @@ static bool hasIncompleteRecordByValue(mlir::Type ty) {
   return false;
 }
 
-/// Whether \p ty has the x87 80-bit format, wrapped in LongDoubleType or not.
-static bool isX87LongDouble(mlir::Type ty) {
+/// Whether \p ty has the x87 double extended format, wrapped in LongDoubleType
+/// or not.
+static bool isX87DoubleExtended(mlir::Type ty) {
   auto fpTy = dyn_cast<cir::FPTypeInterface>(ty);
   return fpTy &&
          &fpTy.getFloatSemantics() == &llvm::APFloat::x87DoubleExtended();
 }
 
-/// Whether \p ty is, or holds by value, an x87 long double, alone or as the
-/// element of a _Complex or a vector.
-static bool holdsX87LongDouble(mlir::Type ty) {
+/// Whether \p ty is, or holds by value, an x87 double extended value, alone or
+/// as the element of a _Complex or a vector.
+static bool holdsX87DoubleExtended(mlir::Type ty) {
   if (isa<cir::FPTypeInterface>(ty))
-    return isX87LongDouble(ty);
+    return isX87DoubleExtended(ty);
   if (auto complexTy = dyn_cast<cir::ComplexType>(ty))
-    return holdsX87LongDouble(complexTy.getElementType());
+    return isX87DoubleExtended(complexTy.getElementType());
   if (auto vecTy = dyn_cast<cir::VectorType>(ty))
-    return holdsX87LongDouble(vecTy.getElementType());
+    return isX87DoubleExtended(vecTy.getElementType());
   if (auto arrTy = dyn_cast<cir::ArrayType>(ty))
-    return arrTy.getSize() && holdsX87LongDouble(arrTy.getElementType());
+    return arrTy.getSize() && holdsX87DoubleExtended(arrTy.getElementType());
   if (auto recTy = dyn_cast<cir::RecordType>(ty))
-    return llvm::any_of(recTy.getMembers(), holdsX87LongDouble);
+    return llvm::any_of(recTy.getMembers(), holdsX87DoubleExtended);
   return false;
 }
 
@@ -308,20 +309,20 @@ static bool isSupportedType(mlir::Type ty, const 
DataLayout &dl) {
         return false;
       // A union is moved as a value of its storage type, and an x87 value
       // stores fewer bytes than it occupies, so the bytes another member
-      // holds past them would be lost.  A long double stores its first 10
-      // bytes, so it can share a union with other long double members and
+      // holds past them would be lost.  A double extended member stores its
+      // first 10 bytes, so it can share a union with other such members and
       // with members whose data ends within those bytes, provided it is the
       // storage type.  An x87 value held any other way, in a _Complex, a
       // vector, a record or an array, is measured at its full storage, which
       // reaches past them.
-      if (members.size() > 1 && llvm::any_of(members, holdsX87LongDouble)) {
+      if (members.size() > 1 && llvm::any_of(members, holdsX87DoubleExtended)) 
{
         uint64_t x87StoreBytes = llvm::APFloat::semanticsSizeInBits(
                                      llvm::APFloat::x87DoubleExtended()) /
                                  8;
-        if (!isX87LongDouble(
+        if (!isX87DoubleExtended(
                 mlir::cast<cir::UnionType>(recTy).getUnionStorageType(dl)) ||
             !llvm::all_of(members, [&](mlir::Type m) {
-              return isX87LongDouble(m) ||
+              return isX87DoubleExtended(m) ||
                      getDataExtentInBytes(m, dl) <= x87StoreBytes;
             }))
           return false;
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
index 1003172ebd9c61..098c8ec8e2e333 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
@@ -205,10 +205,11 @@ module attributes {
   // CHECK: not yet implemented for type '!cir.vector<3 x 
!cir.long_double<!cir.f80>>
 
   // A union is moved as a value of its storage type, so a member holding data
-  // past the 10 bytes a long double stores would lose it.  A member reaching
-  // byte 11 is refused next to a long double, and so is an x87 value held any
-  // other way, in a record, a _Complex, a vector or an array, even next to one
-  // of the same type.  A record holding such a union is refused with it.
+  // past the 10 bytes an x87 double extended value stores would lose it.  A
+  // member reaching byte 11 is refused next to one, and so is a double
+  // extended value held any other way, in a record, a _Complex, a vector or an
+  // array, even next to one of the same type.  A record holding such a union
+  // is refused with it.
   cir.func @take_union_x87_scalar(%arg0: !rec_UX87Scalar) {
     cir.return
   }
@@ -221,9 +222,9 @@ module attributes {
 
   // CHECK: not yet implemented for type '!cir.union<"UX87Nested"
 
-  // A member that ties the long double on alignment and size, listed first,
-  // becomes the storage type in its place, so the union is refused even
-  // though that member's data ends within the 10 bytes.
+  // A member that ties the double extended member on alignment and size,
+  // listed first, becomes the storage type in its place, so the union is
+  // refused even though that member's data ends within the 10 bytes.
   cir.func @take_union_x87_displaced(%arg0: !rec_UX87Displaced) {
     cir.return
   }

>From 4de3071bf06e80698ef6182572a3e819c3309b5a Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Fri, 9 Oct 2026 16:05:14 -0700
Subject: [PATCH 3/3] Test x87 vectors whose element count is not a power of
 two

Assisted-by: Cursor / Claude Opus 5.5
---
 .../call-conv-lowering-x86_64-x87-vector.c    | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c 
b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
index dc1623f2df4d72..128ce8c2014079 100644
--- a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-x87-vector.c
@@ -260,6 +260,61 @@ struct S4 call_s4(struct S4 s) { return take_s4(s); }
 // LLVM-AVX: call void @take_s4(ptr dead_on_unwind writable sret(%struct.S4) 
align 64 %{{[^,)]+}}, ptr noundef byval(%struct.S4) align 64 %{{[^,)]+}})
 // LLVM-AVX512: call <4 x x86_fp80> @take_s4(<4 x x86_fp80> %{{[^,)]+}})
 
+// Three elements take 48 bytes, which clang rounds up to 64, so they pass in
+// registers only with AVX-512.  CIRGen cannot load or store a three-element
+// vector yet, so these values only flow from a call into a return or another
+// call.
+typedef long double ld3 __attribute__((ext_vector_type(3)));
+ld3 src_ld3(void);
+void sink_ld3(ld3);
+
+ld3 fwd_ld3(void) { return src_ld3(); }
+
+// CIR: cir.func {{.*}}@fwd_ld3() -> !cir.vector<3 x 
!cir.long_double<!cir.f80>>
+// CIR: cir.call @src_ld3() : () -> !cir.vector<3 x !cir.long_double<!cir.f80>>
+// LLVM: define dso_local <3 x x86_fp80> @fwd_ld3()
+// LLVM: call <3 x x86_fp80> @src_ld3()
+
+void pass_ld3(void) { sink_ld3(src_ld3()); }
+
+// CIR-SSE: cir.call @sink_ld3(%{{.+}}) : (!cir.ptr<!cir.vector<3 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<3 
x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @sink_ld3(%{{.+}}) : (!cir.ptr<!cir.vector<3 x 
!cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, llvm.byval = !cir.vector<3 
x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @sink_ld3(%{{.+}}) : (!cir.vector<3 x 
!cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM-SSE: call void @sink_ld3(ptr noundef byval(<3 x x86_fp80>) align 64 
%{{[^,)]+}})
+// LLVM-AVX: call void @sink_ld3(ptr noundef byval(<3 x x86_fp80>) align 64 
%{{[^,)]+}})
+// LLVM-AVX512: call void @sink_ld3(<3 x x86_fp80> noundef %{{[^,)]+}})
+
+struct S3 { ld3 v; };
+struct S3 src_s3(void);
+void sink_s3(struct S3);
+
+struct S3 fwd_s3(void) { return src_s3(); }
+
+// CIR-SSE: cir.func {{.*}}@fwd_s3(%arg0: !cir.ptr<!rec_S3> {llvm.align = 64 : 
i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_S3, llvm.writable} 
loc({{[^)]+}}))
+// CIR-AVX: cir.func {{.*}}@fwd_s3(%arg0: !cir.ptr<!rec_S3> {llvm.align = 64 : 
i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_S3, llvm.writable} 
loc({{[^)]+}}))
+// CIR-AVX512: cir.func {{.*}}@fwd_s3() -> !cir.vector<3 x !cir.f80>
+// LLVM-SSE: define dso_local void @fwd_s3(ptr dead_on_unwind noalias writable 
sret(%struct.S3) align 64 %{{[^,)]+}})
+// LLVM-AVX: define dso_local void @fwd_s3(ptr dead_on_unwind noalias writable 
sret(%struct.S3) align 64 %{{[^,)]+}})
+// LLVM-AVX512: define dso_local <3 x x86_fp80> @fwd_s3()
+
+void pass_s3(void) { sink_s3(src_s3()); }
+
+// CIR-SSE: cir.call @sink_s3(%{{.+}}) : (!cir.ptr<!rec_S3> {llvm.align = 64 : 
i64, llvm.byval = !rec_S3, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @sink_s3(%{{.+}}) : (!cir.ptr<!rec_S3> {llvm.align = 64 : 
i64, llvm.byval = !rec_S3, llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @sink_s3(%{{.+}}) : (!cir.vector<3 x !cir.f80>) -> ()
+// LLVM-SSE: call void @sink_s3(ptr noundef byval(%struct.S3) align 64 
%{{[^,)]+}})
+// LLVM-AVX: call void @sink_s3(ptr noundef byval(%struct.S3) align 64 
%{{[^,)]+}})
+// LLVM-AVX512: call void @sink_s3(<3 x x86_fp80> %{{[^,)]+}})
+
+void var_ld3(void) { var(1, src_ld3()); }
+
+// CIR-SSE: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.ptr<!cir.vector<3 x !cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, 
llvm.byval = !cir.vector<3 x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.ptr<!cir.vector<3 x !cir.long_double<!cir.f80>>> {llvm.align = 64 : i64, 
llvm.byval = !cir.vector<3 x !cir.long_double<!cir.f80>>, llvm.noundef}) -> ()
+// CIR-AVX512: cir.call @var(%{{.+}}, %{{.+}}) : (!s32i {llvm.noundef}, 
!cir.vector<3 x !cir.long_double<!cir.f80>> {llvm.noundef}) -> ()
+// LLVM-SSE: call void (i32, ...) @var(i32 noundef 1, ptr noundef byval(<3 x 
x86_fp80>) align 64 %{{[^,)]+}})
+// LLVM-AVX: call void (i32, ...) @var(i32 noundef 1, ptr noundef byval(<3 x 
x86_fp80>) align 64 %{{[^,)]+}})
+// LLVM-AVX512: call void (i32, ...) @var(i32 noundef 1, <3 x x86_fp80> 
noundef %{{[^,)]+}})
+
 // A long double stores its first 10 bytes, so it can share a union with
 // members whose data ends within them, counting neither a record's tail
 // padding nor an empty array.

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to