Skip to content

Commit aedc949

Browse files
mstorsjorazmser
authored andcommitted
[clang] Skip stores in init for fields that are empty structs
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
1 parent 7405899 commit aedc949

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "CGCall.h"
1515
#include "ABIInfo.h"
16+
#include "ABIInfoImpl.h"
1617
#include "CGBlocks.h"
1718
#include "CGCXXABI.h"
1819
#include "CGCleanup.h"
@@ -5781,9 +5782,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
57815782
DestIsVolatile = false;
57825783
}
57835784

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

57885794
return convertTempToRValue(DestPtr, RetTy, SourceLocation());
57895795
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple powerpc64le-windows-gnu -emit-llvm -o - %s | FileCheck %s
3+
4+
// An empty struct is handled as a struct with a dummy i8, on all targets.
5+
// Most targets treat an empty struct return value as essentially void - but
6+
// some don't. (Currently, at least x86_64-windows-* and powerpc64le-* don't
7+
// treat it as void.)
8+
//
9+
// When intializing a struct with such a no_unique_address member, make sure we
10+
// don't write the dummy i8 into the struct where there's no space allocated for
11+
// it.
12+
//
13+
// This can only be tested with targets that don't treat empty struct returns as
14+
// void.
15+
16+
struct S {};
17+
S f();
18+
struct Z {
19+
int x;
20+
[[no_unique_address]] S y;
21+
Z();
22+
};
23+
Z::Z() : x(111), y(f()) {}
24+
25+
// CHECK: define {{.*}} @_ZN1ZC2Ev
26+
// CHECK: %call = call i8 @_Z1fv()
27+
// CHECK-NEXT: ret void
28+
29+
30+
// Check that the constructor for an empty member gets called with the right
31+
// 'this' pointer.
32+
33+
struct S2 {
34+
S2();
35+
};
36+
struct Z2 {
37+
int x;
38+
[[no_unique_address]] S2 y;
39+
Z2();
40+
};
41+
Z2::Z2() : x(111) {}
42+
43+
// CHECK: define {{.*}} @_ZN2Z2C2Ev(ptr {{.*}} %this)
44+
// CHECK: %this.addr = alloca ptr
45+
// CHECK: store ptr %this, ptr %this.addr
46+
// CHECK: %this1 = load ptr, ptr %this.addr
47+
// CHECK: call void @_ZN2S2C1Ev(ptr {{.*}} %this1)

0 commit comments

Comments
 (0)