llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Adam Smith (adams381)

<details>
<summary>Changes</summary>

CIR lays a C++ empty class out as a record that is nothing but padding, and 
isSupportedType rejected every padded record, so any signature naming one 
failed the pass with an NYI. Ordinary C++ hits this constantly through tag 
dispatch, allocators, and empty bases.

Implement empty C++ class support. Record emptiness as an is_empty flag on 
cir.record_layout, filled by CIRGen from the AST, and have the pass read that 
flag rather than inspect the type. The type cannot answer on its own, because 
`struct { int : 3; }` and `struct { unsigned char c; }` lower to identical CIR, 
yet classic CodeGen drops the first from the signature and passes the second in 
a register. An empty record then maps with no fields, so the classifier drops 
it on its own.

Assisted-by: Cursor / claude-opus-5


---

Patch is 49.07 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/214742.diff


13 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+12-4) 
- (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp (+4-1) 
- (modified) clang/lib/CIR/CodeGen/TargetInfo.cpp (+56) 
- (modified) clang/lib/CIR/CodeGen/TargetInfo.h (+14) 
- (modified) clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp (+57-13) 
- (added) clang/test/CIR/CodeGen/call-conv-lowering-x86_64-empty.cpp (+149) 
- (modified) clang/test/CIR/CodeGen/record-type-metadata.cpp (+58-4) 
- (modified) clang/test/CIR/IR/invalid-record-layout.cir (+2-2) 
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir 
(+80-4) 
- (added) clang/test/CIR/Transforms/abi-lowering/x86_64-empty-class.cir (+248) 
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-struct-indirect.cir 
(+1-1) 
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-union.cir (+6-3) 
- (modified) clang/unittests/CIR/RecordTypeMetadataTest.cpp (+19-5) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 71585cd83fb66..a10db4fa2419f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -143,6 +143,10 @@ def CIR_RecordLayoutAttr : CIR_Attr<"RecordLayout", 
"record_layout", [
     - `record_align_in_bytes`: from `ASTRecordLayout::getAlignment()`.
       Needed because CIR's DataLayout cannot account for
       `__attribute__((aligned(N)))`.
+    - `is_empty`: whether the record carries no data for argument passing
+      (mirrors `isEmptyRecordForABI`).  The record type cannot answer this on
+      its own, because an empty class and a one-byte record of data can lower
+      to the same members.
 
     Example:
     ```
@@ -151,11 +155,13 @@ def CIR_RecordLayoutAttr : CIR_Attr<"RecordLayout", 
"record_layout", [
         "Trivial" = #cir.record_layout<
           arg_passing_kind = can_pass_in_regs,
           has_trivial_dtor = true,
-          record_align = 4>,
+          record_align = 4,
+          is_empty = false>,
         "NonTrivialDtor" = #cir.record_layout<
           arg_passing_kind = cannot_pass_in_regs,
           has_trivial_dtor = false,
-          record_align = 4>
+          record_align = 4,
+          is_empty = false>
       }
     }
     ```
@@ -164,14 +170,16 @@ def CIR_RecordLayoutAttr : CIR_Attr<"RecordLayout", 
"record_layout", [
   let parameters = (ins
     EnumParameter<CIR_ArgPassingKind>:$arg_passing_kind,
     "bool":$has_trivial_dtor,
-    "uint64_t":$record_align
+    "uint64_t":$record_align,
+    "bool":$is_empty
   );
 
   let assemblyFormat = [{
     `<`
       `arg_passing_kind` `=` $arg_passing_kind `,`
       `has_trivial_dtor` `=` $has_trivial_dtor `,`
-      `record_align` `=` $record_align
+      `record_align` `=` $record_align `,`
+      `is_empty` `=` $is_empty
     `>`
   }];
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index e4476b88ec6f7..9d12b14db4d91 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -13,6 +13,7 @@
 #include "CIRGenBuilder.h"
 #include "CIRGenModule.h"
 #include "CIRGenTypes.h"
+#include "TargetInfo.h"
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -759,10 +760,12 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
       hasTrivialDestructor = cxxRD->hasTrivialDestructor();
     const auto &astLayout = astContext.getASTRecordLayout(rd);
     uint64_t recordAlignInBytes = astLayout.getAlignment().getQuantity();
+    bool isEmpty =
+        isEmptyRecordForABI(astContext, astContext.getCanonicalTagType(rd));
 
     cgm.addRecordLayout(ty->getName(), cir::RecordLayoutAttr::get(
                                            mlirCtx, apk, hasTrivialDestructor,
-                                           recordAlignInBytes));
+                                           recordAlignInBytes, isEmpty));
   }
 
   auto rl = std::make_unique<CIRGenRecordLayout>(
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp 
b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index ba7eeb29dd252..6847761ab5e5a 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -45,6 +45,62 @@ bool clang::CIRGen::isEmptyFieldForLayout(const ASTContext 
&context,
   return isEmptyRecordForLayout(context, fd->getType());
 }
 
+bool clang::CIRGen::isEmptyFieldForABI(const ASTContext &context,
+                                       const FieldDecl *fd) {
+  if (fd->isUnnamedBitField())
+    return true;
+
+  QualType ft = fd->getType();
+
+  // An array of empty records is empty, and a zero-length array always is.
+  bool wasArray = false;
+  while (const ConstantArrayType *at = context.getAsConstantArrayType(ft)) {
+    if (at->isZeroSize())
+      return true;
+    ft = at->getElementType();
+    wasArray = true;
+  }
+
+  const auto *rt = ft->getAsCanonical<RecordType>();
+  if (!rt)
+    return false;
+
+  // A C++ record field is never empty under the Itanium ABI unless
+  // [[no_unique_address]] makes it so, and that exception covers a record
+  // rather than an array of them.
+  if (isa<CXXRecordDecl>(rt->getDecl()) &&
+      (wasArray || !fd->hasAttr<NoUniqueAddressAttr>()))
+    return false;
+
+  return isEmptyRecordForABI(context, ft);
+}
+
+bool clang::CIRGen::isEmptyRecordForABI(const ASTContext &context, QualType t) 
{
+  const auto *rd = t->getAsRecordDecl();
+  if (!rd)
+    return false;
+  if (rd->hasFlexibleArrayMember())
+    return false;
+
+  if (const auto *cxxrd = dyn_cast<CXXRecordDecl>(rd)) {
+    // A vtable pointer is neither a base nor a field, so clang's predicate
+    // calls a polymorphic class empty and leans on its callers rejecting one 
as
+    // non-trivially-copyable beforehand.  This answer is recorded as metadata
+    // and read without that precondition, so rule it out here instead.
+    if (cxxrd->isDynamicClass())
+      return false;
+
+    for (const auto &i : cxxrd->bases())
+      if (!isEmptyRecordForABI(context, i.getType()))
+        return false;
+  }
+
+  for (const auto *i : rd->fields())
+    if (!isEmptyFieldForABI(context, i))
+      return false;
+  return true;
+}
+
 namespace {
 
 class AMDGPUABIInfo : public ABIInfo {
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h 
b/clang/lib/CIR/CodeGen/TargetInfo.h
index 308d472234f99..fca7df45a627f 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -37,6 +37,20 @@ bool isEmptyFieldForLayout(const ASTContext &context, const 
FieldDecl *fd);
 /// if the [[no_unique_address]] attribute would have made them empty.
 bool isEmptyRecordForLayout(const ASTContext &context, QualType t);
 
+/// isEmptyFieldForABI - Return true if the field is "empty" for argument
+/// passing.  An unnamed bit-field of any width qualifies, as does an empty
+/// record, though a C++ one only under [[no_unique_address]] and never through
+/// an array of them.  This differs from isEmptyFieldForLayout, which counts
+/// only a zero-width bit-field, because a narrower unnamed bit-field still
+/// occupies no ABI class even though it takes up layout space.
+bool isEmptyFieldForABI(const ASTContext &context, const FieldDecl *fd);
+
+/// isEmptyRecordForABI - Return true if a record contains only empty base
+/// classes and fields, and so contributes no data to argument passing.  What
+/// that means for a signature is the caller's to decide: an empty record small
+/// enough is dropped, but one past two eightbytes is still passed indirectly.
+bool isEmptyRecordForABI(const ASTContext &context, QualType t);
+
 class CIRGenFunction;
 
 class TargetCIRGenInfo {
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 193c2b6f4a9dc..bd53d4e9bcaa8 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -69,8 +69,9 @@ namespace {
 // dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
 // consumes.  Integer (including `_BitInt` up to 128 bits) / pointer / bool /
 // f32 / f64 scalars and struct / union / array aggregates are handled.
-// `_Complex`, vectors, wider floats, packed or padded records, and a union no
-// member of which spans its declared size are reported NYI by
+// `_Complex`, vectors, wider floats, packed records, padded records the layout
+// metadata does not call empty, a union no member of which spans its declared
+// size, and a union with an empty member are reported NYI by
 // classifyX86_64Function so an unsupported signature fails the pass instead of
 // being misclassified.
 
//===----------------------------------------------------------------------===//
@@ -86,6 +87,27 @@ static bool recordCanPassInRegs(ModuleOp modOp, 
cir::RecordType recTy) {
   return layout.getArgPassingKind() == cir::ArgPassingKind::CanPassInRegs;
 }
 
+/// Whether a record carries no data for argument passing.  The answer has to
+/// come from the module's record-layout metadata, because an empty C++ class
+/// and a record holding one byte of data lower to the same members.  A record
+/// with no entry, such as one CXXABILowering synthesized, is treated as
+/// carrying data.
+static bool recordIsEmpty(ModuleOp modOp, cir::RecordType recTy) {
+  auto layout = cir::tryGetRecordLayout(modOp, recTy.getName());
+  return layout && layout.getIsEmpty();
+}
+
+/// Whether a union member holds no data, either because it is an empty record
+/// or an array of them.  An array is stripped because the ABI library reduces
+/// a union to a single member and coerces from that member's bytes, and it
+/// cannot see that an array of empty records supplies none.
+static bool unionMemberIsEmpty(ModuleOp modOp, mlir::Type ty) {
+  while (auto arrTy = dyn_cast<cir::ArrayType>(ty))
+    ty = arrTy.getElementType();
+  auto recTy = dyn_cast<cir::RecordType>(ty);
+  return recTy && recordIsEmpty(modOp, recTy);
+}
+
 /// A record's declared alignment, which the ABI uses for the byval and sret
 /// alignment of an indirect argument.  DataLayout derives alignment from the
 /// members, so it cannot see `__attribute__((aligned(N)))`.  The declared 
value
@@ -105,7 +127,8 @@ static llvm::Align recordDeclaredAlign(ModuleOp modOp, 
cir::RecordType recTy,
 /// Aggregates: a complete struct or union whose members are all themselves
 /// supported, or an array of a supported element type.  Everything else is
 /// reported NYI at the reject() choke point in classifyX86_64Function.
-static bool isSupportedType(mlir::Type ty, const DataLayout &dl) {
+static bool isSupportedType(mlir::Type ty, const DataLayout &dl,
+                            ModuleOp modOp) {
   // A pointer is only handled in the default address space (null) or an
   // already-lowered target address space.  A LangAddressSpaceAttr must be
   // lowered before this pass, so reject it rather than silently dropping it.
@@ -130,7 +153,7 @@ static bool isSupportedType(mlir::Type ty, const DataLayout 
&dl) {
     return intTy.getWidth() <= 64 || intTy.getWidth() == 128;
   }
   if (auto arrTy = dyn_cast<cir::ArrayType>(ty))
-    return isSupportedType(arrTy.getElementType(), dl);
+    return isSupportedType(arrTy.getElementType(), dl, modOp);
   if (auto recTy = dyn_cast<cir::RecordType>(ty)) {
     // An incomplete record has no layout to classify, and a packed one needs
     // pad-aware eightbyte classification this bridge does not implement.
@@ -156,14 +179,25 @@ static bool isSupportedType(mlir::Type ty, const 
DataLayout &dl) {
         };
         if (!llvm::any_of(members, spansRecord))
           return false;
+        // Classic sizes a union's coercion from the bytes that hold data, so 
an
+        // empty member contributes none.  The library instead reduces the 
union
+        // to one member, picked by alignment and then by size, and coerces 
from
+        // that member: an empty one can win either comparison and widen the
+        // coercion past what classic emits.
+        auto isEmptyMember = [&](mlir::Type m) {
+          return unionMemberIsEmpty(modOp, m);
+        };
+        if (llvm::any_of(members, isEmptyMember))
+          return false;
       }
-    } else if (recTy.getPadded()) {
-      // A struct's padding is a member the classifier would have to recognize
-      // as padding rather than data, which is not implemented.
+    } else if (recTy.getPadded() && !recordIsEmpty(modOp, recTy)) {
+      // Padding the classifier would have to tell apart from data is not
+      // implemented.  An empty record has no data to confuse it with.
       return false;
     }
-    return llvm::all_of(recTy.getMembers(),
-                        [&](mlir::Type m) { return isSupportedType(m, dl); });
+    return llvm::all_of(recTy.getMembers(), [&](mlir::Type m) {
+      return isSupportedType(m, dl, modOp);
+    });
   }
   return false;
 }
@@ -251,6 +285,16 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
         llvm::TypeSize sizeBits = llvm::TypeSize::getFixed(
             dl.getTypeSizeInBits(type).getFixedValue());
         llvm::Align align = recordDeclaredAlign(modOp, recTy, dl);
+
+        // An empty record holds no user data, so map it with no fields and let
+        // the classifier reach Ignore on its own.  The size still decides
+        // between that and the memory class, which an empty class past two
+        // eightbytes lands in.
+        if (recordIsEmpty(modOp, recTy))
+          return tb.getRecordType(
+              /*Fields=*/{}, sizeBits, align, 
llvm::abi::StructPacking::Default,
+              /*BaseClasses=*/{}, /*VirtualBaseClasses=*/{}, flags);
+
         SmallVector<llvm::abi::FieldInfo> fields;
         fields.reserve(recTy.getMembers().size());
 
@@ -265,8 +309,8 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
                                  llvm::abi::StructPacking::Default, flags);
         }
 
-        // isSupportedType rejects packed and padded structs, so every field
-        // here sits at its naturally-aligned offset.
+        // An accepted non-empty struct is never padded, so every field here
+        // sits at its naturally-aligned offset.
         uint64_t offsetBits = 0;
         for (mlir::Type fieldTy : recTy.getMembers()) {
           const llvm::abi::Type *mappedField =
@@ -307,7 +351,7 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
 /// Indirect: an aggregate that does not fit in registers is passed via a
 /// pointer (sret for returns, byval for arguments).
 ///
-/// Ignore: a void return, or a zero-field record dropped from the signature.
+/// Ignore: a void return, or an empty record dropped from the signature.
 static std::optional<ArgClassification>
 convertABIArgInfo(const llvm::abi::ArgInfo &info, MLIRContext *ctx,
                   mlir::Type origTy) {
@@ -388,7 +432,7 @@ static std::optional<FunctionClassification> 
classifyX86_64Signature(
   bool voidRet = isa<cir::VoidType>(retCIR);
 
   auto reject = [&](mlir::Type t) -> bool {
-    if (isSupportedType(t, dl))
+    if (isSupportedType(t, dl, modOp))
       return false;
     emitError()
         << "x86_64 calling-convention lowering not yet implemented for type "
diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-empty.cpp 
b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-empty.cpp
new file mode 100644
index 0000000000000..3927a8ce3b8ae
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-empty.cpp
@@ -0,0 +1,149 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir \
+// RUN:   -clangir-enable-call-conv-lowering -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 \
+// RUN:   -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-CIR --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,LLVM-OGCG --input-file=%t.ll %s
+
+struct Empty {};
+struct EmptyMem { Empty e; };
+struct HasEmptyBase : Empty {};
+struct Derived : EmptyMem { int i; };
+struct Aligned {} __attribute__((aligned(16)));
+struct NoUnique { [[no_unique_address]] Empty a, b, c; };
+struct NoUniqueOne { [[no_unique_address]] Empty e; };
+struct UnnamedBits { int : 3; };
+struct Reserved { unsigned : 32; };
+struct OneByte { unsigned char c; };
+struct ArrOfEmpty { Empty a[2]; };
+struct HasEmpty { int x; Empty e; };
+struct EmptyFirst { Empty e; int x; };
+struct alignas(32) Big32 {};
+union UBits { unsigned : 3; };
+union UNone {};
+
+// An empty class is passed in no register at all.
+int takeEmpty(Empty v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z9takeEmpty5Emptyi(%arg0: !s32i {{.*}}) -> (!s32i
+// LLVM: define dso_local noundef i32 @_Z9takeEmpty5Emptyi(i32 noundef 
%{{[^,]+}})
+
+// A plain empty member leaves the record non-empty under the Itanium rule, so
+// this is dropped because the member contributes no eightbyte.
+int takeEmptyMem(EmptyMem v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z12takeEmptyMem8EmptyMemi(%arg0: !s32i {{.*}}) -> 
(!s32i
+// LLVM: define dso_local noundef i32 @_Z12takeEmptyMem8EmptyMemi(i32 noundef 
%{{[^,]+}})
+
+// Emptiness does follow a base class.
+int takeHasEmptyBase(HasEmptyBase v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z16takeHasEmptyBase12HasEmptyBasei(%arg0: !s32i 
{{.*}}) -> (!s32i
+// LLVM: define dso_local noundef i32 @_Z16takeHasEmptyBase12HasEmptyBasei(i32 
noundef %{{[^,]+}})
+
+// The empty base takes layout space, so the int sits at offset 4 and the
+// eightbyte covering both coerces to i64.
+int takeDerived(Derived v) { return v.i; }
+
+// CIR: cir.func {{.*}}@_Z11takeDerived7Derived(%arg0: !u64i {{.*}}) -> (!s32i
+// LLVM: define dso_local noundef i32 @_Z11takeDerived7Derived(i64 %{{[^,]+}})
+
+// An alignment attribute widens the padding, and several [[no_unique_address]]
+// members do too, but neither makes the record carry data.
+int takeAligned(Aligned v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z11takeAligned7Alignedi(%arg0: !s32i {{.*}}) -> (!s32i
+// LLVM: define dso_local noundef i32 @_Z11takeAligned7Alignedi(i32 noundef 
%{{[^,]+}})
+
+int takeNoUnique(NoUnique v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z12takeNoUnique8NoUniquei(%arg0: !s32i {{.*}}) -> 
(!s32i
+// LLVM: define dso_local noundef i32 @_Z12takeNoUnique8NoUniquei(i32 noundef 
%{{[^,]+}})
+
+// [[no_unique_address]] is what lets a single empty member count as empty.
+int takeNoUniqueOne(NoUniqueOne v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z15takeNoUniqueOne11NoUniqueOnei(%arg0: !s32i {{.*}}) 
-> (!s32i
+// LLVM: define dso_local noundef i32 @_Z15takeNoUniqueOne11NoUniqueOnei(i32 
noundef %{{[^,]+}})
+
+// A record holding only unnamed bit-fields carries no data either, though it
+// occupies layout space.  It lowers to the same CIR type as OneByte below, so
+// the record-layout metadata is what separates them.
+int takeUnnamedBits(UnnamedBits v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z15takeUnnamedBits11UnnamedBitsi(%arg0: !s32i {{.*}}) 
-> (!s32i
+// LLVM: define dso_local noundef i32 @_Z15takeUnnamedBits11UnnamedBitsi(i32 
noundef %{{[^,]+}})
+
+int takeReserved(Reserved v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z12takeReserved8Reservedi(%arg0: !s32i {{.*}}) -> 
(!s32i
+// LLVM: define dso_local noundef i32 @_Z12takeReserved8Reservedi(i32 noundef 
%{{[^,]+}})
+
+// A byte of real data keeps its register.
+int takeOneByte(OneByte v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z11takeOneByte7OneBytei(%arg0: !u8i {{.*}}, %arg1: 
!s32i {{.*}}) -> (!s32i
+// LLVM: define dso_local noundef i32 @_Z11takeOneByte7OneBytei(i8 %{{[^,]+}}, 
i32 noundef %{{[^,]+}})
+
+// An array of empty records leaves its record non-empty, and the array still
+// contributes no eightbyte.
+int takeArrOfEmpty(ArrOfEmpty v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z14takeArrOfEmpty10ArrOfEmptyi(%arg0: !s32i {{.*}}) 
-> (!s32i
+// LLVM: define dso_local noundef i32 @_Z14takeArrOfEmpty10ArrOfEmptyi(i32 
noundef %{{[^,]+}})
+
+// An empty member contributes no eightbyte, so only the int is classified.
+int takeHasEmpty(HasEmpty v) { return v.x; }
+
+// CIR: cir.func {{.*}}@_Z12takeHasEmpty8HasEmpty(%arg0: !s32i {{.*}}) -> 
(!s32i
+// LLVM: define dso_local noundef i32 @_Z12takeHasEmpty8HasEmpty(i32 
%{{[^,]+}})
+
+int takeEmptyFirst(EmptyFirst v) { return v.x; }
+
+// CIR: cir.func {{.*}}@_Z14takeEmptyFirst10EmptyFirst(%arg0: !u64i {{.*}}) -> 
(!s32i
+// LLVM: define dso_local noundef i32 @_Z14takeEmptyFirst10EmptyFirst(i64 
%{{[^,]+}})
+
+// Past two eightbytes SysV says memory whatever the content, so an empty class
+// this size is passed indirectly at its declared alignment.
+int takeBig32(Big32 v, int k) { return k; }
+
+// CIR: cir.func {{.*}}@_Z9takeBig325Big32i(%arg0: !cir.ptr<!rec_Big32> 
{llvm.align = 32 : i64, llvm.byval = !rec_Big32, llvm.noalias, 
llvm.noundef}{{.*}}, %arg1: !s32i {{.*}}) -> (!s32i
+// LLVM-CIR: define dso_local noundef i32 @_Z9takeBig325Big32i(ptr noalias 
noundef byval(%struct.Big32) align 32 %{{[^,]+}}, i32 noundef %{{[^,]+}})
+/...
[truncated]

``````````

</details>


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

Reply via email to