Skip to content

Commit

Permalink
[clang] Skip stores in init for fields that are empty structs
Browse files Browse the repository at this point in the history
An empty struct is handled as a struct with a dummy i8, on all targets.

Most targets treat an empty struct return value as essentially
void - but some don't. (Currently, at least x86_64-windows-* and
powerpc64le-* don't treat it as void.)

When intializing a struct with such a no_unique_address member,
make sure we don't write the dummy i8 into the struct where there's
no space allocated for it.

Previously it would clobber the actual valid data of the struct.

Fixes llvm#64253, and
possibly llvm#64077
and llvm#64427 as well.

We should omit the store for any empty record (not only ones
declared with no_unique_address); we can have a situation where a
class doesn't have the no_unique_address attribute, but is embedded
in an outer struct with the no_unique_address attribute - like this:

    struct S {};
    S f();
    struct S2 : public S { S2();};
    S2::S2() : S(f()) {}
    struct S3 { int x; [[no_unique_address]] S2 y; S3(); };
    S3::S3() : x(1), y() {}

Here, the problematic store (which this patch omits) is in
the constructor of S2. In the case of S3, S2 has no valid storage
and aliases x - thus the constructor of S2 should omit the dummy
store.

Differential Revision: https://reviews.llvm.org/D157332
  • Loading branch information
mstorsjo authored and razmser committed Oct 3, 2023
1 parent 41ae144 commit a486de2
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 3 deletions.
12 changes: 9 additions & 3 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "CGCall.h"
#include "ABIInfo.h"
#include "ABIInfoImpl.h"
#include "CGBlocks.h"
#include "CGCXXABI.h"
#include "CGCleanup.h"
Expand Down Expand Up @@ -5781,9 +5782,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
DestIsVolatile = false;
}

// If the value is offset in memory, apply the offset now.
Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI);
CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this);
// An empty record can overlap other data (if declared with
// no_unique_address); omit the store for such types - as there is no
// actual data to store.
if (!isEmptyRecord(getContext(), RetTy, true)) {
// If the value is offset in memory, apply the offset now.
Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI);
CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this);
}

return convertTempToRValue(DestPtr, RetTy, SourceLocation());
}
Expand Down
47 changes: 47 additions & 0 deletions clang/test/CodeGenCXX/ctor-empty-nounique.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple powerpc64le-windows-gnu -emit-llvm -o - %s | FileCheck %s

// An empty struct is handled as a struct with a dummy i8, on all targets.
// Most targets treat an empty struct return value as essentially void - but
// some don't. (Currently, at least x86_64-windows-* and powerpc64le-* don't
// treat it as void.)
//
// When intializing a struct with such a no_unique_address member, make sure we
// don't write the dummy i8 into the struct where there's no space allocated for
// it.
//
// This can only be tested with targets that don't treat empty struct returns as
// void.

struct S {};
S f();
struct Z {
int x;
[[no_unique_address]] S y;
Z();
};
Z::Z() : x(111), y(f()) {}

// CHECK: define {{.*}} @_ZN1ZC2Ev
// CHECK: %call = call i8 @_Z1fv()
// CHECK-NEXT: ret void


// Check that the constructor for an empty member gets called with the right
// 'this' pointer.

struct S2 {
S2();
};
struct Z2 {
int x;
[[no_unique_address]] S2 y;
Z2();
};
Z2::Z2() : x(111) {}

// CHECK: define {{.*}} @_ZN2Z2C2Ev(ptr {{.*}} %this)
// CHECK: %this.addr = alloca ptr
// CHECK: store ptr %this, ptr %this.addr
// CHECK: %this1 = load ptr, ptr %this.addr
// CHECK: call void @_ZN2S2C1Ev(ptr {{.*}} %this1)

0 comments on commit a486de2

Please sign in to comment.