diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index ec39e926889b9..e222ba8943f74 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -633,6 +633,7 @@ BUILTIN(__builtin_vsscanf, "icC*RcC*Ra", "FS:1:") BUILTIN(__builtin_thread_pointer, "v*", "nc") BUILTIN(__builtin_launder, "v*v*", "ntE") LANGBUILTIN(__builtin_is_constant_evaluated, "b", "nE", CXX_LANG) +LANGBUILTIN(__builtin_clear_padding, "v.", "n", CXX_LANG) // GCC exception builtins BUILTIN(__builtin_eh_return, "vzv*", "r") // FIXME: Takes intptr_t, not size_t! diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 65d9862621061..006afcd42c9e4 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -60,9 +60,14 @@ #include "llvm/Support/ScopedPrinter.h" #include "llvm/TargetParser/AArch64TargetParser.h" #include "llvm/TargetParser/X86TargetParser.h" +#include #include #include + + +#include + using namespace clang; using namespace CodeGen; using namespace llvm; @@ -2456,6 +2461,139 @@ static RValue EmitHipStdParUnsupportedBuiltin(CodeGenFunction *CGF, return RValue::get(CGF->Builder.CreateCall(UBF, Args)); } +template +void RecursivelyClearPaddingImpl(CodeGenFunction &CGF, Value *Ptr, QualType Ty, size_t CurrentStartOffset, size_t &RunningOffset, T&& WriteZeroAtOffset); + +template +void ClearPaddingStruct(CodeGenFunction &CGF, Value *Ptr, QualType Ty, StructType *ST, + size_t CurrentStartOffset, size_t &RunningOffset, T&& WriteZeroAtOffset) { + std::cerr << "\n struct! " << ST->getName().data() << std::endl; + auto SL = CGF.CGM.getModule().getDataLayout().getStructLayout(ST); + + auto R = dyn_cast(Ty->getAsRecordDecl()); + if(!R) { + std::cerr << "\n not a CXXRecordDecl" << std::endl; + + } + const ASTRecordLayout &ASTLayout = CGF.getContext().getASTRecordLayout(R); + for (auto Base : R->bases()) { + std::cerr << "\n\n base!" << std::endl; + // Zero padding between base elements. + auto BaseRecord = cast(Base.getType()->getAsRecordDecl()); + auto Offset = static_cast( + ASTLayout.getBaseClassOffset(BaseRecord).getQuantity()); + // Recursively zero out base classes. + auto Index = SL->getElementContainingOffset(Offset); + Value *Idx = CGF.Builder.getSize(Index); + llvm::Type *CurrentBaseType = CGF.ConvertTypeForMem(Base.getType()); + Value *BaseElement = CGF.Builder.CreateGEP(CurrentBaseType, Ptr, Idx); + RecursivelyClearPaddingImpl(CGF, BaseElement, Base.getType(), CurrentStartOffset + Offset, RunningOffset, WriteZeroAtOffset); + } + + size_t NumFields = std::distance(R->field_begin(), R->field_end()); + auto CurrentField = R->field_begin(); + for (size_t I = 0; I < NumFields; ++I, ++CurrentField) { + // Size needs to be in bytes so we can compare it later. + auto Offset = ASTLayout.getFieldOffset(I) / 8; + auto Index = SL->getElementContainingOffset(Offset); + Value *Idx = CGF.Builder.getSize(Index); + llvm::Type *CurrentFieldType = CGF.ConvertTypeForMem(CurrentField->getType()); + Value *Element = CGF.Builder.CreateGEP(CurrentFieldType, Ptr, Idx); + RecursivelyClearPaddingImpl(CGF, Element, CurrentField->getType(), CurrentStartOffset + Offset, RunningOffset, WriteZeroAtOffset); + } +} + +template +void ClearPaddingConstantArray(CodeGenFunction &CGF, Value *Ptr, llvm::Type *Type, ConstantArrayType const *AT, + size_t CurrentStartOffset, size_t &RunningOffset, T&& WriteZeroAtOffset) { + for (size_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue(); + ++ArrIndex) { + + QualType ElementQualType = AT->getElementType(); + + auto *ElementRecord = ElementQualType->getAsRecordDecl(); + if(!ElementRecord){ + std::cerr<< "\n\n null!" << std::endl; + } + auto ElementAlign =ElementRecord? + CGF.getContext().getASTRecordLayout(ElementRecord).getAlignment(): + CGF.getContext().getTypeAlignInChars(ElementQualType); + + std::cerr<< "\n\n align: " << ElementAlign.getQuantity() << std::endl; + + // Value *Idx = CGF.Builder.getSize(0); + // Value *ArrayElement = CGF.Builder.CreateGEP(ElementType, Ptr, Idx); + + Address FieldElementAddr{Ptr, Type, ElementAlign}; + + auto Element = + CGF.Builder.CreateConstArrayGEP(FieldElementAddr, ArrIndex); + auto *ElementType = CGF.ConvertTypeForMem(ElementQualType); + auto AllocSize = CGF.CGM.getModule().getDataLayout().getTypeAllocSize(ElementType); + std::cerr << "\n\n clearing array index! " << ArrIndex << std::endl; + RecursivelyClearPaddingImpl(CGF, Element.getPointer(), ElementQualType, CurrentStartOffset + ArrIndex * AllocSize.getKnownMinValue(), RunningOffset, WriteZeroAtOffset); + } +} + +template +void RecursivelyClearPaddingImpl(CodeGenFunction &CGF, Value *Ptr, QualType Ty, size_t CurrentStartOffset, + size_t& RunningOffset, T&& WriteZeroAtOffset) { + + std::cerr << "\n\n zero padding before current ["<< RunningOffset << ", " << CurrentStartOffset<< ")"<< std::endl; + for (; RunningOffset < CurrentStartOffset; ++RunningOffset) { + WriteZeroAtOffset(RunningOffset); + } + auto Type = CGF.ConvertTypeForMem(Ty); + auto Size = CGF.CGM.getModule() + .getDataLayout() + .getTypeSizeInBits(Type) + .getKnownMinValue() / 8; + + if (auto *AT = dyn_cast(Ty)) { + ClearPaddingConstantArray(CGF, Ptr, Type, AT, CurrentStartOffset, RunningOffset, WriteZeroAtOffset); + } + else if (auto *ST = dyn_cast(Type); ST && Ty->isRecordType()) { + ClearPaddingStruct(CGF, Ptr, Ty, ST, CurrentStartOffset, RunningOffset, WriteZeroAtOffset); + } else if (Ty->isAtomicType()) { + RecursivelyClearPaddingImpl(CGF, Ptr, Ty.getAtomicUnqualifiedType(), CurrentStartOffset, RunningOffset, WriteZeroAtOffset); + } else { + std::cerr << "\n\n increment running offset from: " << RunningOffset << " to " << RunningOffset + Size << std::endl; + RunningOffset = std::max(RunningOffset, CurrentStartOffset + static_cast(Size)); + } + +} + +static void RecursivelyClearPadding(CodeGenFunction &CGF, Value *Ptr, + QualType Ty) { + auto *I8Ptr = CGF.Builder.CreateBitCast(Ptr, CGF.Int8PtrTy); + auto *Zero = ConstantInt::get(CGF.Int8Ty, 0); + auto WriteZeroAtOffset = [&](size_t Offset) { + auto *Index = ConstantInt::get(CGF.IntTy, Offset); + auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); + CGF.Builder.CreateAlignedStore( + Zero, Element, + CharUnits::One().alignmentAtOffset(CharUnits::fromQuantity(Offset))); + }; + + + size_t RunningOffset = 0; + + RecursivelyClearPaddingImpl(CGF, Ptr, Ty, 0, RunningOffset, WriteZeroAtOffset); + + // Clear tail padding + auto Type = CGF.ConvertTypeForMem(Ty); + + auto Size = CGF.CGM.getModule() + .getDataLayout() + .getTypeAllocSize(Type) + .getKnownMinValue(); + + std::cerr << "\n\n zero tail padding ["<< RunningOffset << ", " << Size << ")"<< std::endl; + for (; RunningOffset < Size; ++RunningOffset) { + WriteZeroAtOffset(RunningOffset); + } +} + RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -4315,6 +4453,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(Ptr); } + case Builtin::BI__builtin_clear_padding: { + const Expr *Op = E->getArg(0); + Value *Address = EmitScalarExpr(Op); + auto PointeeTy = Op->getType()->getPointeeType(); + RecursivelyClearPadding(*this, Address, PointeeTy); + return RValue::get(nullptr); + } case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_sub: case Builtin::BI__sync_fetch_and_or: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 77c8334f3ca25..cf34171a56399 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2327,6 +2327,25 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, } case Builtin::BI__builtin_launder: return SemaBuiltinLaunder(*this, TheCall); + case Builtin::BI__builtin_clear_padding: { + const Expr *PtrArg = TheCall->getArg(0)->IgnoreParenImpCasts(); + const QualType PtrArgType = PtrArg->getType(); + if (!PtrArgType->isPointerType()) { + Diag(PtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible) + << PtrArgType << "pointer" << 1 << 0 << 3 << 1 << PtrArgType + << "pointer"; + return ExprError(); + } + if (PtrArgType->getPointeeType().isConstQualified()) { + Diag(PtrArg->getBeginLoc(), diag::err_typecheck_assign_const) + << TheCall->getSourceRange() << 5 /*ConstUnknown*/; + return ExprError(); + } + if (RequireCompleteType(PtrArg->getBeginLoc(), PtrArgType->getPointeeType(), + diag::err_typecheck_decl_incomplete_type)) + return ExprError(); + break; + } case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_add_1: case Builtin::BI__sync_fetch_and_add_2: diff --git a/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp b/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp new file mode 100644 index 0000000000000..54455e6699849 --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + +struct alignas(4) Foo { + char a; + alignas(2) char b; +}; + +struct alignas(4) Bar { + char c; + alignas(2) char d; +}; + +struct alignas(4) Baz : Foo { + char e; + Bar f; +}; + +// Baz structure: +// "a", PAD_1, "b", PAD_2, "c", PAD_3, PAD_4, PAD_5, "c", PAD_6, "d", PAD_7 +// %struct.Baz = type { %struct.Foo, i8, [3 x i8], %struct.Bar } +// %struct.Foo = type { i8, i8, i8, i8 } +// %struct.Bar = type { i8, i8, i8, i8 } + +// CHECK-LABEL: define void @_Z7testBazP3Baz(%struct.Baz* %baz) +// CHECK: [[ADDR:%.*]] = alloca %struct.Baz* +// CHECK: store %struct.Baz* %baz, %struct.Baz** [[ADDR]] +// CHECK: [[BAZ:%.*]] = load %struct.Baz*, %struct.Baz** [[ADDR]] +// CHECK: [[BAZ_RAW_PTR:%.*]] = bitcast %struct.Baz* [[BAZ]] to i8* + +// CHECK: [[FOO_BASE:%.*]] = getelementptr inbounds %struct.Baz, %struct.Baz* [[BAZ]], i32 0, i32 0 +// CHECK: [[FOO_RAW_PTR:%.*]] = bitcast %struct.Foo* [[FOO_BASE]] to i8* +// CHECK: [[PAD_1:%.*]] = getelementptr i8, i8* [[FOO_RAW_PTR]], i32 1 +// CHECK: store i8 0, i8* [[PAD_1]] +// CHECK: [[PAD_2:%.*]] = getelementptr i8, i8* [[FOO_RAW_PTR]], i32 3 +// CHECK: store i8 0, i8* [[PAD_2]] + +// CHECK: [[PAD_3:%.*]] = getelementptr i8, i8* [[BAZ_RAW_PTR]], i32 5 +// CHECK: store i8 0, i8* [[PAD_3]] +// CHECK: [[PAD_4:%.*]] = getelementptr i8, i8* [[BAZ_RAW_PTR]], i32 6 +// CHECK: store i8 0, i8* [[PAD_4]] +// CHECK: [[PAD_5:%.*]] = getelementptr i8, i8* [[BAZ_RAW_PTR]], i32 7 +// CHECK: store i8 0, i8* [[PAD_5]] + +// CHECK: [[BAR_MEMBER:%.*]] = getelementptr inbounds %struct.Baz, %struct.Baz* [[BAZ]], i32 0, i32 3 +// CHECK: [[BAR_RAW_PTR:%.*]] = bitcast %struct.Bar* [[BAR_MEMBER]] to i8* +// CHECK: [[PAD_6:%.*]] = getelementptr i8, i8* [[BAR_RAW_PTR]], i32 1 +// CHECK: store i8 0, i8* [[PAD_6]] +// CHECK: [[PAD_7:%.*]] = getelementptr i8, i8* [[BAR_RAW_PTR]], i32 3 +// CHECK: store i8 0, i8* [[PAD_7]] +// CHECK: ret void +void testBaz(Baz *baz) { + __builtin_clear_padding(baz); +} + +struct UnsizedTail { + int size; + alignas(8) char buf[]; + + UnsizedTail(int size) : size(size) {} +}; + +// UnsizedTail structure: +// "size", PAD_1, PAD_2, PAD_3, PAD_4 +// %struct.UnsizedTail = type { i32, [4 x i8], [0 x i8] } + +// CHECK-LABEL: define void @_Z15testUnsizedTailP11UnsizedTail(%struct.UnsizedTail* %u) +// CHECK: [[U_ADDR:%.*]] = alloca %struct.UnsizedTail* +// CHECK: store %struct.UnsizedTail* %u, %struct.UnsizedTail** [[U_ADDR]] +// CHECK: [[U:%.*]] = load %struct.UnsizedTail*, %struct.UnsizedTail** [[U_ADDR]] +// CHECK: [[U_RAW_PTR:%.*]] = bitcast %struct.UnsizedTail* [[U]] to i8* +// CHECK: [[PAD_1:%.*]] = getelementptr i8, i8* [[U_RAW_PTR]], i32 4 +// CHECK: store i8 0, i8* [[PAD_1]] +// CHECK: [[PAD_2:%.*]] = getelementptr i8, i8* [[U_RAW_PTR]], i32 5 +// CHECK: store i8 0, i8* [[PAD_2]] +// CHECK: [[PAD_3:%.*]] = getelementptr i8, i8* [[U_RAW_PTR]], i32 6 +// CHECK: store i8 0, i8* [[PAD_3]] +// CHECK: [[PAD_4:%.*]] = getelementptr i8, i8* [[U_RAW_PTR]], i32 7 +// CHECK: store i8 0, i8* [[PAD_4]] +// CHECK: ret void +void testUnsizedTail(UnsizedTail *u) { + __builtin_clear_padding(u); +} + +struct ArrOfStructsWithPadding { + Bar bars[2]; +}; + +// ArrOfStructsWithPadding structure: +// "c" (1), PAD_1, "d" (1), PAD_2, "c" (2), PAD_3, "d" (2), PAD_4 +// %struct.ArrOfStructsWithPadding = type { [2 x %struct.Bar] } + +// CHECK-LABEL: define void @_Z27testArrOfStructsWithPaddingP23ArrOfStructsWithPadding(%struct.ArrOfStructsWithPadding* %arr) +// CHECK: [[ARR_ADDR:%.*]] = alloca %struct.ArrOfStructsWithPadding* +// CHECK: store %struct.ArrOfStructsWithPadding* %arr, %struct.ArrOfStructsWithPadding** [[ARR_ADDR]] +// CHECK: [[ARR:%.*]] = load %struct.ArrOfStructsWithPadding*, %struct.ArrOfStructsWithPadding** [[ARR_ADDR]] +// CHECK: [[BARS:%.*]] = getelementptr inbounds %struct.ArrOfStructsWithPadding, %struct.ArrOfStructsWithPadding* [[ARR]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = getelementptr inbounds [2 x %struct.Bar], [2 x %struct.Bar]* [[BARS]], i64 0, i64 0 +// CHECK: [[FIRST_RAW_PTR:%.*]] = bitcast %struct.Bar* [[FIRST]] to i8* +// CHECK: [[PAD_1:%.*]] = getelementptr i8, i8* [[FIRST_RAW_PTR]], i32 1 +// CHECK: store i8 0, i8* [[PAD_1]] +// CHECK: [[PAD_2:%.*]] = getelementptr i8, i8* %4, i32 3 +// CHECK: store i8 0, i8* [[PAD_2]] +// CHECK: [[SECOND:%.*]] = getelementptr inbounds [2 x %struct.Bar], [2 x %struct.Bar]* [[BARS]], i64 0, i64 1 +// CHECK: [[SECOND_RAW_PTR:%.*]] = bitcast %struct.Bar* [[SECOND]] to i8* +// CHECK: [[PAD_3:%.*]] = getelementptr i8, i8* [[SECOND_RAW_PTR]], i32 1 +// CHECK: store i8 0, i8* [[PAD_3]] +// CHECK: [[PAD_4:%.*]] = getelementptr i8, i8* [[SECOND_RAW_PTR]], i32 3 +// CHECK: store i8 0, i8* [[PAD_4]] +// CHECK: ret void +void testArrOfStructsWithPadding(ArrOfStructsWithPadding *arr) { + __builtin_clear_padding(arr); +} diff --git a/clang/test/CodeGenCXX/builtin-clear-padding.cpp b/clang/test/CodeGenCXX/builtin-clear-padding.cpp new file mode 100644 index 0000000000000..b9060e00d3203 --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-clear-padding.cpp @@ -0,0 +1,556 @@ +// RUN: mkdir -p %t +// RUN: %clang++ %s -o %t/run +// RUN: %t/run + +#include +#include +#include +#include + +template +void print_bytes(const T *object) +{ + auto size = sizeof(T); + const unsigned char * const bytes = reinterpret_cast(object); + size_t i; + + fprintf(stderr, "[ "); + for(i = 0; i < size; i++) + { + fprintf(stderr, "%02x ", bytes[i]); + } + fprintf(stderr, "]\n"); +} + +template +struct alignas(A1) BasicWithPadding { + T x; + alignas(A2) T y; +}; + +template +struct alignas(A1) SpacedArrayMembers { + T x[N]; + alignas(A2) char c; + T y[N]; +}; + +template +struct alignas(A1) PaddedPointerMembers { + T *x; + alignas(A2) T *y; +}; + +template +struct alignas(A1) ThreeMembers { + T x; + alignas(A2) T y; + alignas(A3) T z; +}; + +template +struct Normal { + T a; + T b; +}; + +template +struct X { + T x; +}; + +template +struct Z { + T z; +}; + +template +struct YZ : public Z { + alignas(A) T y; +}; + +template +struct alignas(A1) HasBase : public X, public YZ { + T a; + alignas(A2) T b; +}; + +template +void testAllForType(T a, T b, T c, T d) { + using B = BasicWithPadding; + B basic1; + memset(&basic1, 0, sizeof(B)); + basic1.x = a; + basic1.y = b; + B basic2; + memset(&basic2, 42, sizeof(B)); + basic2.x = a; + basic2.y = b; + assert(memcmp(&basic1, &basic2, sizeof(B)) != 0); + __builtin_clear_padding(&basic2); + assert(memcmp(&basic1, &basic2, sizeof(B)) == 0); + using A = SpacedArrayMembers; + A arr1; + memset(&arr1, 0, sizeof(A)); + arr1.x[0] = a; + arr1.x[1] = b; + arr1.y[0] = c; + arr1.y[1] = d; + A arr2; + memset(&arr2, 42, sizeof(A)); + arr2.x[0] = a; + arr2.x[1] = b; + arr2.y[0] = c; + arr2.y[1] = d; + arr2.c = 0; + assert(memcmp(&arr1, &arr2, sizeof(A)) != 0); + __builtin_clear_padding(&arr2); + assert(memcmp(&arr1, &arr2, sizeof(A)) == 0); + + using P = PaddedPointerMembers; + P ptr1; + memset(&ptr1, 0, sizeof(P)); + ptr1.x = &a; + ptr1.y = &b; + P ptr2; + memset(&ptr2, 42, sizeof(P)); + ptr2.x = &a; + ptr2.y = &b; + assert(memcmp(&ptr1, &ptr2, sizeof(P)) != 0); + __builtin_clear_padding(&ptr2); + assert(memcmp(&ptr1, &ptr2, sizeof(P)) == 0); + + using Three = ThreeMembers; + Three three1; + memset(&three1, 0, sizeof(Three)); + three1.x = a; + three1.y = b; + three1.z = c; + Three three2; + memset(&three2, 42, sizeof(Three)); + three2.x = a; + three2.y = b; + three2.z = c; + __builtin_clear_padding(&three2); + assert(memcmp(&three1, &three2, sizeof(Three)) == 0); + + using N = Normal; + N normal1; + memset(&normal1, 0, sizeof(N)); + normal1.a = a; + normal1.b = b; + N normal2; + memset(&normal2, 42, sizeof(N)); + normal2.a = a; + normal2.b = b; + __builtin_clear_padding(&normal2); + assert(memcmp(&normal1, &normal2, sizeof(N)) == 0); + + using H = HasBase; + H base1; + memset(&base1, 0, sizeof(H)); + base1.a = a; + base1.b = b; + base1.x = c; + base1.y = d; + base1.z = a; + H base2; + memset(&base2, 42, sizeof(H)); + base2.a = a; + base2.b = b; + base2.x = c; + base2.y = d; + base2.z = a; + assert(memcmp(&base1, &base2, sizeof(H)) != 0); + __builtin_clear_padding(&base2); + unsigned i = 0; + assert(memcmp(&base1, &base2, sizeof(H)) == 0); +} + +struct UnsizedTail { + int size; + alignas(8) char buf[]; + + UnsizedTail(int size) : size(size) {} +}; + +void otherTests() { + const size_t size1 = sizeof(UnsizedTail) + 4; + char buff1[size1]; + char buff2[size1]; + memset(buff1, 0, size1); + memset(buff2, 42, size1); + auto *u1 = new (buff1) UnsizedTail(4); + u1->buf[0] = 1; + u1->buf[1] = 2; + u1->buf[2] = 3; + u1->buf[3] = 4; + auto *u2 = new (buff2) UnsizedTail(4); + u2->buf[0] = 1; + u2->buf[1] = 2; + u2->buf[2] = 3; + u2->buf[3] = 4; + assert(memcmp(u1, u2, sizeof(UnsizedTail)) != 0); + __builtin_clear_padding(u2); + assert(memcmp(u1, u2, sizeof(UnsizedTail)) == 0); + + using B = BasicWithPadding<8, 4, char>; + auto *basic1 = new B; + memset(basic1, 0, sizeof(B)); + basic1->x = 1; + basic1->y = 2; + auto *basic2 = new B; + memset(basic2, 42, sizeof(B)); + basic2->x = 1; + basic2->y = 2; + assert(memcmp(basic1, basic2, sizeof(B)) != 0); + __builtin_clear_padding(basic2); + assert(memcmp(basic1, basic2, sizeof(B)) == 0); + delete basic2; + delete basic1; + + using B = BasicWithPadding<8, 4, char>; + B *basic3 = new B; + memset(basic3, 0, sizeof(B)); + basic3->x = 1; + basic3->y = 2; + B *basic4 = new B; + memset(basic4, 42, sizeof(B)); + basic4->x = 1; + basic4->y = 2; + assert(memcmp(basic3, basic4, sizeof(B)) != 0); + __builtin_clear_padding(const_cast(basic4)); + assert(memcmp(basic3, basic4, sizeof(B)) == 0); + delete basic4; + delete basic3; +} + +struct Foo { + int x; + int y; +}; + +typedef float Float4Vec __attribute__((ext_vector_type(4))); +typedef float Float3Vec __attribute__((ext_vector_type(3))); + +void primitiveTests() +{ + // no padding + { + int i1 = 42, i2 = 42; + __builtin_clear_padding(&i1); // does nothing + assert(i1 == 42); + assert(memcmp(&i1, &i2, sizeof(int)) == 0); + } + + // long double + { + long double d1, d2; + memset(&d1, 42, sizeof(long double)); + memset(&d2, 0, sizeof(long double)); + + d1 = 3.0L; + d2 = 3.0L; + + __builtin_clear_padding(&d1); + assert(d1 == 3.0L); + assert(memcmp(&d1, &d2, sizeof(long double)) == 0); + } +} + +void structTests() +{ + // no_unique_address + { + struct S1 { + int x; + char c; + }; + + struct S2{ + [[no_unique_address]] S1 s; + bool b; + }; + + S2 s1, s2; + memset(&s1, 42, sizeof(S2)); + memset(&s2, 0, sizeof(S2)); + + s1.s.x = 4; + s1.s.c = 'a'; + s1.b = true; + s2.s.x = 4; + s2.s.c = 'a'; + s2.b = true; + + assert(memcmp(&s1, &s2, sizeof(S2)) != 0); + __builtin_clear_padding(&s1); + assert(s1.s.x == 4); + assert(s1.s.c == 'a'); + assert(s1.b == true); + + assert(memcmp(&s1, &s2, sizeof(S2)) == 0); + } + + // struct with long double + { + struct S{ + long double l; + bool b; + }; + + S s1, s2; + memset(&s1, 42, sizeof(S)); + memset(&s2, 0, sizeof(S)); + + s1.l = 3.0L; + s1.b = true; + s2.l = 3.0L; + s2.b = true; + + assert(memcmp(&s1, &s2, sizeof(S)) != 0); + __builtin_clear_padding(&s1); + assert(s1.l == 3.0L); + assert(s1.b == true); + assert(memcmp(&s1, &s2, sizeof(S)) == 0); + } + + // EBO + { + struct Empty{}; + struct B { + int i; + }; + struct S : Empty, B { + bool b; + }; + + S s1, s2; + memset(&s1, 42, sizeof(S)); + memset(&s2, 0, sizeof(S)); + + s1.i = 4; + s1.b = true; + s2.i = 4; + s2.b = true; + + assert(memcmp(&s1, &s2, sizeof(S)) != 0); + __builtin_clear_padding(&s1); + assert(s1.i == 4); + assert(s1.b == true); + assert(memcmp(&s1, &s2, sizeof(S)) == 0); + } + + // padding between bases + { + struct B1 { + char c1; + }; + struct B2 { + alignas(4) char c2; + }; + + struct S: B1, B2 {}; + + S s1, s2; + memset(&s1, 42, sizeof(S)); + memset(&s2, 0, sizeof(S)); + + s1.c1 = 'a'; + s1.c2 = 'b'; + s2.c1 = 'a'; + s2.c2 = 'b'; + + assert(memcmp(&s1, &s2, sizeof(S)) != 0); + __builtin_clear_padding(&s1); + assert(s1.c1 == 'a'); + assert(s1.c2 == 'b'); + assert(memcmp(&s1, &s2, sizeof(S)) == 0); + } + + // padding after last base + { + struct B1 { + char c1; + }; + struct B2 { + char c2; + }; + + struct S: B1, B2 { + alignas(4) char c3; + }; + + S s1, s2; + memset(&s1, 42, sizeof(S)); + memset(&s2, 0, sizeof(S)); + + s1.c1 = 'a'; + s1.c2 = 'b'; + s1.c3 = 'c'; + s2.c1 = 'a'; + s2.c2 = 'b'; + s2.c3 = 'c'; + + assert(memcmp(&s1, &s2, sizeof(S)) != 0); + __builtin_clear_padding(&s1); + assert(s1.c1 == 'a'); + assert(s1.c2 == 'b'); + assert(s1.c3 == 'c'); + assert(memcmp(&s1, &s2, sizeof(S)) == 0); + } + + testAllForType<32, 16, char>(11, 22, 33, 44); + testAllForType<64, 32, char>(4, 5, 6, 7); + testAllForType<32, 16, volatile char>(11, 22, 33, 44); + testAllForType<64, 32, volatile char>(4, 5, 6, 7); + testAllForType<32, 16, int>(0, 1, 2, 3); + testAllForType<64, 32, int>(4, 5, 6, 7); + testAllForType<32, 16, volatile int>(0, 1, 2, 3); + testAllForType<64, 32, volatile int>(4, 5, 6, 7); + testAllForType<32, 16, double>(0, 1, 2, 3); + testAllForType<64, 32, double>(4, 5, 6, 7); + testAllForType<32, 16, _ExtInt(28)>(0, 1, 2, 3); + testAllForType<64, 32, _ExtInt(28)>(4, 5, 6, 7); + testAllForType<32, 16, _ExtInt(60)>(0, 1, 2, 3); + testAllForType<64, 32, _ExtInt(60)>(4, 5, 6, 7); + testAllForType<32, 16, _ExtInt(64)>(0, 1, 2, 3); + testAllForType<64, 32, _ExtInt(64)>(4, 5, 6, 7); + testAllForType<32, 16, Foo>(Foo{1, 2}, Foo{3, 4}, Foo{1, 2}, Foo{3, 4}); + testAllForType<64, 32, Foo>(Foo{1, 2}, Foo{3, 4}, Foo{1, 2}, Foo{3, 4}); + testAllForType<256, 128, Float3Vec>(0, 1, 2, 3); + testAllForType<128, 128, Float3Vec>(4, 5, 6, 7); + testAllForType<256, 128, Float4Vec>(0, 1, 2, 3); + testAllForType<128, 128, Float4Vec>(4, 5, 6, 7); + + otherTests(); +} + +void unionTests() { + // different length, do not clear object repr bits of non-active member + { + union u { + int i; + char c; + }; + + u u1, u2; + memset(&u1, 42, sizeof(u)); + memset(&u2, 42, sizeof(u)); + u1.c = '4'; + u2.c = '4'; + + __builtin_clear_padding(&u1); // should have no effect + assert(u1.c == '4'); + + assert(memcmp(&u1, &u2, sizeof(u)) == 0); + } + + // tail padding of longest member + { + struct s { + alignas(8) char c1; + }; + + union u { + s s1; + char c2; + }; + + u u1, u2; + memset(&u1, 42, sizeof(u)); + memset(&u2, 0, sizeof(u)); + + u1.s1.c1 = '4'; + u2.s1.c1 = '4'; + + assert(memcmp(&u1, &u2, sizeof(u)) != 0); + __builtin_clear_padding(&u1); + assert(u1.s1.c1 == '4'); + assert(memcmp(&u1, &u2, sizeof(u)) == 0); + } +} + +void arrayTests() { + // no padding + { + int i1[2] = {1, 2}; + int i2[2] = {1, 2}; + + __builtin_clear_padding(&i1); + assert(i1[0]==1); + assert(i1[1]==2); + assert(memcmp(&i1, &i2, 2*sizeof(int)) == 0); + } + + // long double + { + long double d1[2], d2[2]; + memset(&d1, 42, 2*sizeof(long double)); + memset(&d2, 0, 2*sizeof(long double)); + + d1[0] = 3.0L; + d1[1] = 4.0L; + d2[0] = 3.0L; + d2[1] = 4.0L; + + __builtin_clear_padding(&d1); + assert(d1[0] == 3.0L); + assert(d2[1] == 4.0L); + assert(memcmp(&d1, &d2, 2*sizeof(long double)) == 0); + } + + // struct + { + struct S { + int i1; + char c1; + int i2; + char c2; + }; + + S s1[2], s2[2]; + memset(&s1, 42, 2*sizeof(S)); + memset(&s2, 0, 2*sizeof(S)); + + s1[0].i1 = 1; + s1[0].c1 = 'a'; + s1[0].i2 = 2; + s1[0].c2 = 'b'; + s1[1].i1 = 3; + s1[1].c1 = 'c'; + s1[1].i2 = 4; + s1[1].c2 = 'd'; + + s2[0].i1 = 1; + s2[0].c1 = 'a'; + s2[0].i2 = 2; + s2[0].c2 = 'b'; + s2[1].i1 = 3; + s2[1].c1 = 'c'; + s2[1].i2 = 4; + s2[1].c2 = 'd'; + + assert(memcmp(&s1, &s2, 2*sizeof(S)) != 0); + __builtin_clear_padding(&s1); + + assert(s1[0].i1 == 1); + assert(s1[0].c1 == 'a'); + assert(s1[0].i2 == 2); + assert(s1[0].c2 == 'b'); + assert(s1[1].i1 == 3); + assert(s1[1].c1 == 'c'); + assert(s1[1].i2 == 4); + assert(s1[1].c2 == 'd'); + assert(memcmp(&s1, &s2, 2*sizeof(S)) == 0); + } +} + +int main(int, const char**) { + primitiveTests(); + unionTests(); + structTests(); + arrayTests(); + + return 0; +} diff --git a/clang/test/SemaCXX/builtin-clear-padding.cpp b/clang/test/SemaCXX/builtin-clear-padding.cpp new file mode 100644 index 0000000000000..ea87249c87b0a --- /dev/null +++ b/clang/test/SemaCXX/builtin-clear-padding.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct Foo {}; + +struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}} + +void test(int a, Foo b, void *c, int *d, Foo *e, const Foo *f, Incomplete *g) { + __builtin_clear_padding(a); // expected-error {{passing 'int' to parameter of incompatible type pointer: type mismatch at 1st parameter ('int' vs pointer)}} + __builtin_clear_padding(b); // expected-error {{passing 'Foo' to parameter of incompatible type pointer: type mismatch at 1st parameter ('Foo' vs pointer)}} + __builtin_clear_padding(c); // expected-error {{variable has incomplete type 'void'}} + __builtin_clear_padding(d); // This should not error. + __builtin_clear_padding(e); // This should not error. + __builtin_clear_padding(f); // expected-error {{read-only variable is not assignable}} + __builtin_clear_padding(g); // expected-error {{variable has incomplete type 'Incomplete'}} +}