-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][CGRecordLayout] Remove dependency on isZeroSize #96422
[clang][CGRecordLayout] Remove dependency on isZeroSize #96422
Conversation
In draft for now because I'm still wrapping my head around some of the IR implications of this. Some of them seem suspect. |
db3db1e
to
303fc41
Compare
This is roughly what I expected the patch to look like. Maybe consider adding a couple small wrapper functions around isEmptyRecord/isEmptyField to simplify the uses (and so we can use a name that indicates why the checks exist). |
(Please move out of "draft" when you think this is ready.) |
Updated the PR with some more context. There's still a few instances of |
@llvm/pr-subscribers-backend-x86 @llvm/pr-subscribers-clang-codegen Author: Michael Buch (Michael137) ChangesThis is a follow-up from the conversation starting at #93809 (comment) The root problem that motivated the change are external AST sources that compute This patch proposes to replace the Details Added Patch is 46.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/96422.diff 26 Files Affected:
diff --git a/clang/lib/CodeGen/ABIInfoImpl.cpp b/clang/lib/CodeGen/ABIInfoImpl.cpp
index e9a26abb77837..703e69e889967 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.cpp
+++ b/clang/lib/CodeGen/ABIInfoImpl.cpp
@@ -248,7 +248,7 @@ Address CodeGen::emitMergePHI(CodeGenFunction &CGF, Address Addr1,
return Address(PHI, Addr1.getElementType(), Align);
}
-bool CodeGen::isEmptyField(ASTContext &Context, const FieldDecl *FD,
+bool CodeGen::isEmptyField(const ASTContext &Context, const FieldDecl *FD,
bool AllowArrays, bool AsIfNoUniqueAddr) {
if (FD->isUnnamedBitField())
return true;
@@ -289,8 +289,20 @@ bool CodeGen::isEmptyField(ASTContext &Context, const FieldDecl *FD,
return isEmptyRecord(Context, FT, AllowArrays, AsIfNoUniqueAddr);
}
-bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
- bool AsIfNoUniqueAddr) {
+bool CodeGen::isEmptyFieldForLayout(const ASTContext &Context,
+ const FieldDecl *FD) {
+ if (FD->isZeroLengthBitField(Context))
+ return true;
+
+ if (FD->isUnnamedBitField())
+ return false;
+
+ return isEmptyField(Context, FD, /*AllowArrays=*/false,
+ /*AsIfNoUniqueAddr=*/true);
+}
+
+bool CodeGen::isEmptyRecord(const ASTContext &Context, QualType T,
+ bool AllowArrays, bool AsIfNoUniqueAddr) {
const RecordType *RT = T->getAs<RecordType>();
if (!RT)
return false;
@@ -310,6 +322,11 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
return true;
}
+bool CodeGen::isEmptyRecordForLayout(const ASTContext &Context, QualType T) {
+ return isEmptyRecord(Context, T, /*AllowArrays=*/false,
+ /*AsIfNoUniqueAddr=*/true);
+}
+
const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) {
const RecordType *RT = T->getAs<RecordType>();
if (!RT)
diff --git a/clang/lib/CodeGen/ABIInfoImpl.h b/clang/lib/CodeGen/ABIInfoImpl.h
index 92986fb431646..ec5a5872462c7 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.h
+++ b/clang/lib/CodeGen/ABIInfoImpl.h
@@ -126,17 +126,26 @@ Address emitMergePHI(CodeGenFunction &CGF, Address Addr1,
/// is an unnamed bit-field or an (array of) empty record(s). If
/// AsIfNoUniqueAddr is true, then C++ record fields are considered empty if
/// the [[no_unique_address]] attribute would have made them empty.
-bool isEmptyField(ASTContext &Context, const FieldDecl *FD, bool AllowArrays,
- bool AsIfNoUniqueAddr = false);
+bool isEmptyField(const ASTContext &Context, const FieldDecl *FD,
+ bool AllowArrays, bool AsIfNoUniqueAddr = false);
+
+/// isEmptyFieldForLayout - Return true iff the field is "empty", that is,
+/// either a zero-width bit-field or an empty record.
+bool isEmptyFieldForLayout(const ASTContext &Context, const FieldDecl *FD);
/// isEmptyRecord - Return true iff a structure contains only empty
/// fields. Note that a structure with a flexible array member is not
/// considered empty. If AsIfNoUniqueAddr is true, then C++ record fields are
/// considered empty if the [[no_unique_address]] attribute would have made
/// them empty.
-bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
+bool isEmptyRecord(const ASTContext &Context, QualType T, bool AllowArrays,
bool AsIfNoUniqueAddr = false);
+/// isEmptyRecordForLayout - Return true iff a structure contains only empty
+/// fields. Note, C++ record fields are considered empty if the
+/// [[no_unique_address]] attribute would have made them empty.
+bool isEmptyRecordForLayout(const ASTContext &Context, QualType T);
+
/// isSingleElementStruct - Determine if a structure is a "single
/// element struct", i.e. it has exactly one non-empty field or
/// exactly one field which is itself a single element
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 534f46da74862..60a78e77f78c6 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "ABIInfoImpl.h"
#include "CGCUDARuntime.h"
#include "CGCXXABI.h"
#include "CGCall.h"
@@ -4756,7 +4757,7 @@ static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base,
/// The resulting address doesn't necessarily have the right type.
static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
const FieldDecl *field) {
- if (field->isZeroSize(CGF.getContext()))
+ if (isEmptyFieldForLayout(CGF.getContext(), field))
return emitAddrOfZeroSizeField(CGF, base, field);
const RecordDecl *rec = field->getParent();
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index dffb8ce83b643..1c6f842081ada 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "ABIInfoImpl.h"
#include "CGCXXABI.h"
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
@@ -2437,8 +2438,10 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
// Ignore empty bases.
- if (base->isEmpty() ||
- CGM.getContext().getASTRecordLayout(base).getNonVirtualSize()
+ if (isEmptyRecordForLayout(CGM.getContext(), I.getType()) ||
+ CGM.getContext()
+ .getASTRecordLayout(base)
+ .getNonVirtualSize()
.isZero())
continue;
@@ -2474,7 +2477,7 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
// Ignore empty bases.
- if (base->isEmpty())
+ if (isEmptyRecordForLayout(CGM.getContext(), I.getType()))
continue;
unsigned fieldIndex = layout.getVirtualBaseIndex(base);
diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
index 5169be204c14d..c0f2e4e52f117 100644
--- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
@@ -10,8 +10,9 @@
//
//===----------------------------------------------------------------------===//
-#include "CGRecordLayout.h"
+#include "ABIInfoImpl.h"
#include "CGCXXABI.h"
+#include "CGRecordLayout.h"
#include "CodeGenTypes.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
@@ -384,7 +385,7 @@ void CGRecordLowering::accumulateFields(bool isNonVirtualBaseType) {
Field = accumulateBitFields(isNonVirtualBaseType, Field, FieldEnd);
assert((Field == FieldEnd || !Field->isBitField()) &&
"Failed to accumulate all the bitfields");
- } else if (Field->isZeroSize(Context)) {
+ } else if (isEmptyFieldForLayout(Context, *Field)) {
// Empty fields have no storage.
++Field;
} else {
@@ -634,7 +635,7 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
// non-reusable tail padding.
CharUnits LimitOffset;
for (auto Probe = Field; Probe != FieldEnd; ++Probe)
- if (!Probe->isZeroSize(Context)) {
+ if (!isEmptyFieldForLayout(Context, *Probe)) {
// A member with storage sets the limit.
assert((getFieldBitOffset(*Probe) % CharBits) == 0 &&
"Next storage is not byte-aligned");
@@ -732,7 +733,7 @@ void CGRecordLowering::accumulateBases() {
// Bases can be zero-sized even if not technically empty if they
// contain only a trailing array member.
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
- if (!BaseDecl->isEmpty() &&
+ if (!isEmptyRecordForLayout(Context, Base.getType()) &&
!Context.getASTRecordLayout(BaseDecl).getNonVirtualSize().isZero())
Members.push_back(MemberInfo(Layout.getBaseClassOffset(BaseDecl),
MemberInfo::Base, getStorageType(BaseDecl), BaseDecl));
@@ -880,7 +881,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const {
if (!isNonVirtualBaseType && isOverlappingVBaseABI())
for (const auto &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
- if (BaseDecl->isEmpty())
+ if (isEmptyRecordForLayout(Context, Base.getType()))
continue;
// If the vbase is a primary virtual base of some base, then it doesn't
// get its own storage location but instead lives inside of that base.
@@ -896,7 +897,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const {
void CGRecordLowering::accumulateVBases() {
for (const auto &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
- if (BaseDecl->isEmpty())
+ if (isEmptyRecordForLayout(Context, Base.getType()))
continue;
CharUnits Offset = Layout.getVBaseClassOffset(BaseDecl);
// If the vbase is a primary virtual base of some base, then it doesn't
@@ -1162,7 +1163,7 @@ CodeGenTypes::ComputeRecordLayout(const RecordDecl *D, llvm::StructType *Ty) {
const FieldDecl *FD = *it;
// Ignore zero-sized fields.
- if (FD->isZeroSize(getContext()))
+ if (isEmptyFieldForLayout(getContext(), FD))
continue;
// For non-bit-fields, just check that the LLVM struct offset matches the
diff --git a/clang/test/CodeGen/X86/x86_64-vaarg.c b/clang/test/CodeGen/X86/x86_64-vaarg.c
index d6b885d9fb18c..7d5102f93ca6f 100644
--- a/clang/test/CodeGen/X86/x86_64-vaarg.c
+++ b/clang/test/CodeGen/X86/x86_64-vaarg.c
@@ -56,7 +56,8 @@ typedef struct {
// CHECK: vaarg.end:
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP1]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 8, i1 false)
-// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[RETVAL]], align 8
+// CHECK-NEXT: [[COERCE:%.*]] = getelementptr inbounds [[STRUCT_S1]], ptr [[RETVAL]], i32 0, i32 0
+// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[COERCE]], align 8
// CHECK-NEXT: ret double [[TMP3]]
//
s1 f(int z, ...) {
diff --git a/clang/test/CodeGen/debug-info-packed-struct.c b/clang/test/CodeGen/debug-info-packed-struct.c
index 676cdb38b396f..33b8b4c7e13fb 100644
--- a/clang/test/CodeGen/debug-info-packed-struct.c
+++ b/clang/test/CodeGen/debug-info-packed-struct.c
@@ -2,7 +2,7 @@
// CHECK: %struct.layout3 = type <{ i8, [3 x i8], %struct.size8_pack4, i8, [3 x i8] }>
// CHECK: %struct.layout0 = type { i8, %struct.size8, i8 }
-// CHECK: %struct.layout1 = type <{ i8, %struct.size8_anon, i8, [2 x i8] }>
+// CHECK: %struct.layout1 = type { i8, [8 x i8], i8, [2 x i8] }
// CHECK: %struct.layout2 = type <{ i8, %struct.size8_pack1, i8 }>
// ---------------------------------------------------------------------
diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp
index 88b1834d42d87..16cfe772a4ae5 100644
--- a/clang/test/CodeGen/paren-list-agg-init.cpp
+++ b/clang/test/CodeGen/paren-list-agg-init.cpp
@@ -48,14 +48,13 @@ struct E {
~E() {};
};
-// CHECK-DAG: [[STRUCT_F:%.*]] = type { i8 }
struct F {
F (int i = 1);
F (const F &f) = delete;
F (F &&f) = default;
};
-// CHECK-DAG: [[STRUCT_G:%.*]] = type <{ i32, [[STRUCT_F]], [3 x i8] }>
+// CHECK-DAG: [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
struct G {
int a;
F f;
@@ -78,12 +77,12 @@ namespace gh61145 {
~Vec();
};
- // CHECK-DAG: [[STRUCT_S1:%.*]] = type { [[STRUCT_VEC]] }
+ // CHECK-DAG: [[STRUCT_S1:%.*]] = type { i8 }
struct S1 {
Vec v;
};
- // CHECK-DAG: [[STRUCT_S2:%.*]] = type { [[STRUCT_VEC]], i8 }
+ // CHECK-DAG: [[STRUCT_S2:%.*]] = type { i8, i8 }
struct S2 {
Vec v;
char c;
@@ -377,7 +376,7 @@ void foo18() {
// CHECK-NEXT: [[G:%.*g.*]] = alloca [[STRUCT_G]], align 4
// CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_G]], ptr [[G]], i32 0, i32 0
// CHECK-NEXT: store i32 2, ptr [[A]], align 4
-// CHECK-NEXT: [[F:%.*f.*]] = getelementptr inbounds [[STRUCT_G]], ptr [[G]], i32 0, i32 1
+// CHECK-NEXT: [[F:%.*]] = getelementptr inbounds i8, ptr [[G]], i64 4
// CHECk-NEXT: call void @{{.*F.*}}(ptr noundef nonnull align 1 dereferenceable(1)) [[F]], ie32 noundef 1)
// CHECK: ret void
void foo19() {
@@ -392,9 +391,8 @@ namespace gh61145 {
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S1]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
- // CHECK-NEXT: [[V1:%.*v1.*]] = getelementptr inbounds [[STRUCT_S1]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0
// a.k.a. Vec::Vec(Vec&&)
- // CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[V1]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
+ // CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// a.k.a. S1::~S1()
// CHECK-NEXT: call void @_ZN7gh611452S1D1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]])
// a.k.a.Vec::~Vec()
@@ -413,9 +411,8 @@ namespace gh61145 {
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S2]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
- // CHECK-NEXT: [[V1:%.*v1.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0
// a.k.a. Vec::Vec(Vec&&)
- // CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[V1]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
+ // CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: [[C:%.*c.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[AGG_TMP_ENSURED]], i32 0, i32
// CHECK-NEXT: store i8 0, ptr [[C]], align 1
// a.k.a. S2::~S2()
diff --git a/clang/test/CodeGen/voidptr-vaarg.c b/clang/test/CodeGen/voidptr-vaarg.c
index d023ddf0fb5d2..4f008fd85115a 100644
--- a/clang/test/CodeGen/voidptr-vaarg.c
+++ b/clang/test/CodeGen/voidptr-vaarg.c
@@ -245,7 +245,8 @@ typedef struct {
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
-// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[RETVAL]], align 4
+// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[STRUCT_EMPTY_INT_T]], ptr [[RETVAL]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
// CHECK-NEXT: ret i32 [[TMP0]]
//
empty_int_t empty_int(__builtin_va_list list) {
diff --git a/clang/test/CodeGenCXX/2011-12-19-init-list-ctor.cpp b/clang/test/CodeGenCXX/2011-12-19-init-list-ctor.cpp
index 14557829268ef..3efb8c449c8fa 100644
--- a/clang/test/CodeGenCXX/2011-12-19-init-list-ctor.cpp
+++ b/clang/test/CodeGenCXX/2011-12-19-init-list-ctor.cpp
@@ -19,8 +19,8 @@ struct S {
};
// CHECK: store i32 0, ptr @arr
-// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr @arr, i32 0, i32 1), ptr noundef @.str)
+// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (i8, ptr @arr, i64 4), ptr noundef @.str)
// CHECK: store i32 1, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 1)
-// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 1), i32 0, i32 1), ptr noundef @.str.1)
+// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (i8, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 1), i64 4), ptr noundef @.str.1)
// CHECK: store i32 2, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 2)
-// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (%struct.S, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 2), i32 0, i32 1), ptr noundef @.str.2)
+// CHECK: call void @_ZN1AC1EPKc(ptr {{[^,]*}} getelementptr inbounds (i8, ptr getelementptr inbounds (%struct.S, ptr @arr, i64 2), i64 4), ptr noundef @.str.2)
diff --git a/clang/test/CodeGenCXX/auto-var-init.cpp b/clang/test/CodeGenCXX/auto-var-init.cpp
index e1568bee136e5..b0fc4946040b7 100644
--- a/clang/test/CodeGenCXX/auto-var-init.cpp
+++ b/clang/test/CodeGenCXX/auto-var-init.cpp
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,CHECK-O0
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK-O0,PATTERN,PATTERN-O0
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK-O0,PATTERN,PATTERN-O0,PATTERN-64-O0
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -O1 -emit-llvm -o - | FileCheck %s -check-prefixes=PATTERN,PATTERN-O1
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK-O0,ZERO,ZERO-O0
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=zero %s -O1 -emit-llvm -o - | FileCheck %s -check-prefixes=ZERO,ZERO-O1
-// RUN: %clang_cc1 -std=c++14 -triple i386-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK-O0,PATTERN,PATTERN-O0
+// RUN: %clang_cc1 -std=c++14 -triple i386-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK-O0,PATTERN,PATTERN-O0,PATTERN-32-O0
#pragma clang diagnostic ignored "-Winaccessible-base"
@@ -174,14 +174,17 @@ struct semivolatileinit { int i = 0x11111111; volatile int vi = 0x11111111; };
// PATTERN-O0: @__const.test_base_braces.braces = private unnamed_addr constant %struct.base { ptr inttoptr ([[IPTRT]] [[IPTR]] to ptr) }, align
// PATTERN-O1-NOT: @__const.test_base_braces.braces
struct base { virtual ~base(); };
-// PATTERN-O0: @__const.test_derived_uninit.uninit = private unnamed_addr constant %struct.derived { %struct.base { ptr inttoptr ([[IPTRT]] [[IPTR]] to ptr) } }, align
+// PATTERN-32-O0: @__const.test_derived_uninit.uninit = private unnamed_addr constant %struct.derived { [4 x i8] c"\FF\FF\FF\FF" }, align 4
+// PATTERN-64-O0: @__const.test_derived_uninit.uninit = private unnamed_addr constant %struct.derived { [8 x i8] c"\AA\AA\AA\AA\AA\AA\AA\AA" }, align 8
// PATTERN-O1-NOT: @__const.test_derived_uninit.uninit
-// PATTERN-O0: @__const.test_derived_braces.braces = private unnamed_addr constant %struct.derived { %struct.base { ptr inttoptr ([[IPTRT]] [[IPTR]] to ptr) } }, align
+// PATTERN-32-O0: @__const.test_derived_braces.braces = private unnamed_addr constant %struct.derived { [4 x i8] c"\FF\FF\FF\FF" }, align 4
+// PATTERN-64-O0: @__const.test_derived_braces.braces = private unnamed_addr constant %struct.derived { [8 x i8] c"\AA\AA\AA\AA\AA\AA\AA\AA" }, align 8
// PATTERN-O1-NOT: @__const.test_derived_braces.braces
struct derived : public base {};
-// PATTERN-O0: @__const.test_virtualderived_uninit.uninit = private unnamed_addr constant %struct.virtualderived { %struct.base { ptr inttoptr ([[IPTRT]] [[IPTR]] to ptr) }, %struct.derived { %struct.base { ptr inttoptr ([[IPTRT]] [[IPTR]] to ptr) } } }, align
+// PATERN-O0: @__const.test_virtualderived_uninit.uninit = private unnamed_addr constant %struct.virtualderived { %struct.base { ptr inttoptr (i64 -6148914691236517206 to ptr) }, [8 x i8] c"\AA\AA\AA\AA\AA\AA\AA\AA" }, align 8
// PATTERN-O1-NOT: @__const.test_virtualderived_uninit.uninit
-// PATTERN-O0: @__const.test_virtualderived_braces.braces = private unnam...
[truncated]
|
IIUC, this effectively removes all empty records from LLVM struct types. This makes the IR less "pretty", but these subobjects are notionally empty and consist of only padding bytes (i8 and i8 arrays) at the end of the day. I think that's acceptable. + @rjmccall , does that sound good to you? |
That sounds fine to me as long as we're still emitting projections of them properly (i.e. not just assuming "oh, it's an empty record, we can use whatever pointer we want because it'll never be dereferenced"). |
For CGExprConstant, the code is checking "empty" in the sense of whether there's a corresponding LLVM field. So almost certainly needs changes. Not sure how that isn't causing test failures; maybe there's missing test coverage. For CGClass, it's not directly tied to the LLVM structure layout, but I'm not sure the generated code would be semantically correct if an "empty" field that isn't isEmpty() overlaps with actual data. |
Yea I was pretty sure we'd have to adjust that too. Will try to get some coverage here |
df613b4
to
363be51
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks good, then. Any other concerns?
Had to adjust
I haven't addressed this yet. To clarify, are you referring to |
363be51
to
67caf75
Compare
f02185b
to
a4b15f0
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/154/builds/1436 Here is the relevant piece of the build log for the reference:
|
The FileCheck entry was too strict in this test. Fixed by removing the |
Summary: This is a follow-up from the conversation starting at llvm#93809 (comment) The root problem that motivated the change are external AST sources that compute `ASTRecordLayout`s themselves instead of letting Clang compute them from the AST. One such example is LLDB using DWARF to get the definitive offsets and sizes of C++ structures. Such layouts should be considered correct (modulo buggy DWARF), but various assertions and lowering logic around the `CGRecordLayoutBuilder` relies on the AST having `[[no_unique_address]]` attached to them. This is a layout-altering attribute which is not encoded in DWARF. This causes us LLDB to trip over the various LLVM<->Clang layout consistency checks. There has been precedent for avoiding such layout-altering attributes from affecting lowering with externally-provided layouts (e.g., packed structs). This patch proposes to replace the `isZeroSize` checks in `CGRecordLayoutBuilder` (which roughly means "empty field with [[no_unique_address]]") with checks for `CodeGen::isEmptyField`/`CodeGen::isEmptyRecord`. **Details** The main strategy here was to change the `isZeroSize` check in `CGRecordLowering::accumulateFields` and `CGRecordLowering::accumulateBases` to use the `isEmptyXXX` APIs instead, preventing empty fields from being added to the `Members` and `Bases` structures. The rest of the changes fall out from here, to prevent lookups into these structures (for field numbers or base indices) from failing. Added `isEmptyRecordForLayout` and `isEmptyFieldForLayout` (open to better naming suggestions). The main difference to the existing `isEmptyRecord`/`isEmptyField` APIs, is that the `isEmptyXXXForLayout` counterparts don't have special treatment for `unnamed bitfields`/arrays and also treat fields of empty types as if they had `[[no_unique_address]]` (i.e., just like the `AsIfNoUniqueAddr` in `isEmptyField` does). Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D59822467
Summary: This is a follow-up from the conversation starting at #93809 (comment) The root problem that motivated the change are external AST sources that compute `ASTRecordLayout`s themselves instead of letting Clang compute them from the AST. One such example is LLDB using DWARF to get the definitive offsets and sizes of C++ structures. Such layouts should be considered correct (modulo buggy DWARF), but various assertions and lowering logic around the `CGRecordLayoutBuilder` relies on the AST having `[[no_unique_address]]` attached to them. This is a layout-altering attribute which is not encoded in DWARF. This causes us LLDB to trip over the various LLVM<->Clang layout consistency checks. There has been precedent for avoiding such layout-altering attributes from affecting lowering with externally-provided layouts (e.g., packed structs). This patch proposes to replace the `isZeroSize` checks in `CGRecordLayoutBuilder` (which roughly means "empty field with [[no_unique_address]]") with checks for `CodeGen::isEmptyField`/`CodeGen::isEmptyRecord`. **Details** The main strategy here was to change the `isZeroSize` check in `CGRecordLowering::accumulateFields` and `CGRecordLowering::accumulateBases` to use the `isEmptyXXX` APIs instead, preventing empty fields from being added to the `Members` and `Bases` structures. The rest of the changes fall out from here, to prevent lookups into these structures (for field numbers or base indices) from failing. Added `isEmptyRecordForLayout` and `isEmptyFieldForLayout` (open to better naming suggestions). The main difference to the existing `isEmptyRecord`/`isEmptyField` APIs, is that the `isEmptyXXXForLayout` counterparts don't have special treatment for `unnamed bitfields`/arrays and also treat fields of empty types as if they had `[[no_unique_address]]` (i.e., just like the `AsIfNoUniqueAddr` in `isEmptyField` does). Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251747
/// base classes (per \ref isEmptyRecordForLayout) and fields (per | ||
/// \ref isEmptyFieldForLayout). Note, C++ record fields are considered empty | ||
/// if the [[no_unique_address]] attribute would have made them empty. | ||
bool isEmptyRecordForLayout(const ASTContext &Context, QualType T); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions don't belong here. This file contains helper functions for use in implementations of ABIInfo class.
I guess CGRecordLayout* could be a better place for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put those here just to have them next to isEmptyRecord
/isEmptyField
, which are used outside the implementation of ABIInfo
. But agree with your point. These should probably all live in CGRecordLayout
instead
This change leads to a crash in
|
Thanks for reporting. It looks like this is only happening when compiling as
Looks like when the brace initializer is empty, in C,
Though would like to understand the C vs. C++ significance here first. |
llvm#96422 changed treats empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since llvm#96422 we can get here when compiling `C`, but the `Init` need to exist. This patch adds a null-check to account for this situtation.
llvm#96422 changed treats empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since llvm#96422 we can get here when compiling `C`, but the `Init` need to exist. This patch adds a null-check to account for this situtation.
…109271) In #96422 we started treating empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since #96422 we can get here when compiling `C`, but `Init` need not exist. This patch adds a null-check to account for this situtation.
…lvm#109271) In llvm#96422 we started treating empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since llvm#96422 we can get here when compiling `C`, but `Init` need not exist. This patch adds a null-check to account for this situtation. (cherry picked from commit 2162a18)
…c-crash [clang][CodeGen] Check initializer of zero-size fields for nullptr In llvm#96422 we started treating empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since llvm#96422 we can get here when compiling `C`, but `Init` need not exist. This patch adds a null-check to account for this situtation. (cherry picked from commit 2162a18)
…lvm#109271) In llvm#96422 we started treating empty records as zero-sized for the purpose of layout. In `C`, empty fields were never considered `isZeroSize`, so we would never have tried to call `Init->hasSideEffects` on them. But since llvm#96422 we can get here when compiling `C`, but `Init` need not exist. This patch adds a null-check to account for this situtation.
This is a follow-up from the conversation starting at #93809 (comment)
The root problem that motivated the change are external AST sources that compute
ASTRecordLayout
s themselves instead of letting Clang compute them from the AST. One such example is LLDB using DWARF to get the definitive offsets and sizes of C++ structures. Such layouts should be considered correct (modulo buggy DWARF), but various assertions and lowering logic around theCGRecordLayoutBuilder
relies on the AST having[[no_unique_address]]
attached to them. This is a layout-altering attribute which is not encoded in DWARF. This causes us LLDB to trip over the various LLVM<->Clang layout consistency checks. There has been precedent for avoiding such layout-altering attributes from affecting lowering with externally-provided layouts (e.g., packed structs).This patch proposes to replace the
isZeroSize
checks inCGRecordLayoutBuilder
(which roughly means "empty field with [[no_unique_address]]") with checks forCodeGen::isEmptyField
/CodeGen::isEmptyRecord
.Details
The main strategy here was to change the
isZeroSize
check inCGRecordLowering::accumulateFields
andCGRecordLowering::accumulateBases
to use theisEmptyXXX
APIs instead, preventing empty fields from being added to theMembers
andBases
structures. The rest of the changes fall out from here, to prevent lookups into these structures (for field numbers or base indices) from failing.Added
isEmptyRecordForLayout
andisEmptyFieldForLayout
(open to better naming suggestions). The main difference to the existingisEmptyRecord
/isEmptyField
APIs, is that theisEmptyXXXForLayout
counterparts don't have special treatment forunnamed bitfields
/arrays and also treat fields of empty types as if they had[[no_unique_address]]
(i.e., just like theAsIfNoUniqueAddr
inisEmptyField
does).