Index: clang/lib/CodeGen/CGDecl.cpp =================================================================== --- clang/lib/CodeGen/CGDecl.cpp +++ clang/lib/CodeGen/CGDecl.cpp @@ -1855,6 +1855,13 @@ } } +static bool isEmptyClass(VarDecl const &D) { + const Type *Ty = D.getType().getTypePtr(); + if (const auto *CxxRecordTy = Ty->getAsCXXRecordDecl()) + return CxxRecordTy->isEmpty(); + return false; +} + void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { assert(emission.Variable && "emission was not valid!"); @@ -1902,13 +1909,18 @@ const Address Loc = locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr; - // Note: constexpr already initializes everything correctly. - LangOptions::TrivialAutoVarInitKind trivialAutoVarInit = - (D.isConstexpr() - ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : (D.getAttr() - ? LangOptions::TrivialAutoVarInitKind::Uninitialized - : getContext().getLangOpts().getTrivialAutoVarInit())); + LangOptions::TrivialAutoVarInitKind trivialAutoVarInit; + if (D.isConstexpr()) + // Constexpr already initializes everything correctly. + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else if (D.getAttr()) + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else if (isEmptyClass(D)) + // Empty classes still use a byte, but it's ok not to initialize it as + // it cannot be legitimately accessed. + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + else + trivialAutoVarInit = getContext().getLangOpts().getTrivialAutoVarInit(); auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) { if (trivialAutoVarInit == Index: clang/test/CodeGenCXX/auto-var-init-empty-class.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/auto-var-init-empty-class.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s + +// C++ empty classes still take some memory, but there is no need to initialize +// it has it cannot be accessed. This matches GCC behavior. + +struct S { +}; + +extern "C" void use(struct S*); + +// CHECK-LABEL: @empty_class( +// CHECK: %s = alloca +// CHECK-NEXT: call void @use(ptr noundef %s) +extern "C" void empty_class(void) { + S s; + return use(&s); +} Index: clang/test/CodeGenCXX/auto-var-init.cpp =================================================================== --- clang/test/CodeGenCXX/auto-var-init.cpp +++ clang/test/CodeGenCXX/auto-var-init.cpp @@ -43,9 +43,6 @@ // PATTERN-NOT: undef // ZERO-NOT: undef -// PATTERN-O0: @__const.test_empty_uninit.uninit = private unnamed_addr constant %struct.empty { i8 [[I8]] }, align 1 -// PATTERN-O1-NOT: @__const.test_empty_uninit.uninit -struct empty {}; // PATTERN-O0: @__const.test_small_uninit.uninit = private unnamed_addr constant %struct.small { i8 [[I8]] }, align 1 // PATTERN-O0: @__const.test_small_custom.custom = private unnamed_addr constant %struct.small { i8 42 }, align 1 // ZERO-O0: @__const.test_small_custom.custom = private unnamed_addr constant %struct.small { i8 42 }, align 1 @@ -586,24 +583,6 @@ // CHECK-NOT: !annotation // CHECK-NEXT: call void @{{.*}}used{{.*}}%braces) -TEST_UNINIT(empty, empty); -// CHECK-LABEL: @test_empty_uninit() -// CHECK: %uninit = alloca %struct.empty, align -// CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) -// PATTERN-LABEL: @test_empty_uninit() -// PATTERN-O0: call void @llvm.memcpy{{.*}} @__const.test_empty_uninit.uninit{{.+}}), !annotation [[AUTO_INIT]] -// PATTERN-O1: store i8 [[I8]], {{.*}} align 1, !annotation [[AUTO_INIT]] -// ZERO-LABEL: @test_empty_uninit() -// ZERO-O0: call void @llvm.memset{{.*}}, i8 0,{{.+}}), !annotation [[AUTO_INIT]] -// ZERO-O1: store i8 0, {{.*}} align 1, !annotation [[AUTO_INIT]] - -TEST_BRACES(empty, empty); -// CHECK-LABEL: @test_empty_braces() -// CHECK: %braces = alloca %struct.empty, align -// CHECK-NEXT: call void @llvm.memcpy -// CHECK-NOT: !annotation -// CHECK-NEXT: call void @{{.*}}used{{.*}}%braces) - TEST_UNINIT(small, small); // CHECK-LABEL: @test_small_uninit() // CHECK: %uninit = alloca %struct.small, align