diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -1670,8 +1670,12 @@ return getIndirectResult(Ty, true, State); // Ignore empty structs/unions on non-Windows. - if (!IsWin32StructABI && isEmptyRecord(getContext(), Ty, true)) + if (!IsWin32StructABI && isEmptyRecord(getContext(), Ty, true)) { + // For compatibility with GCC, treat it like a struct with a single char. + if (getContext().getLangOpts().CPlusPlus) + return getIndirectResult(Ty, /*ByVal=*/true, State); return ABIArgInfo::getIgnore(); + } llvm::LLVMContext &LLVMContext = getVMContext(); llvm::IntegerType *Int32 = llvm::Type::getInt32Ty(LLVMContext); @@ -2799,6 +2803,16 @@ if (RD->hasFlexibleArrayMember()) return; + // According to the resolution to A-5 in + // https://itanium-cxx-abi.github.io/cxx-abi/cxx-open.html, empty class by + // value should be treated as a struct containing a single character. + // That's aligned with record layout in AST and compatible with gcc. + // Check https://godbolt.org/z/D1u2MV for the difference in codegen. + if (getContext().getLangOpts().CPlusPlus) + if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) + if (CXXRD->isEmpty()) + return; + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); // Reset Lo class, this will be recomputed. diff --git a/clang/test/CodeGen/empty-class.cpp b/clang/test/CodeGen/empty-class.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/empty-class.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64 | FileCheck --check-prefix=X64 %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i686 | FileCheck --check-prefix=X32 %s + +class Empty {}; + +void bar(Empty *); + +// X64-LABEL: _Z3foo5Empty +// X64-SAME: %class.Empty* byval(%class.Empty) align 8 %e +// X64-NOT: alloca +// X32-LABEL: _Z3foo5Empty +// X32-SAME: %class.Empty* byval(%class.Empty) align 4 %e +// X32-NOT: alloca +void foo(Empty e) { + bar(&e); +}