[libcxx-commits] [clang] [libcxx] [libc++] Add builtin to clear padding bytes (prework for P0528R3) (PR #75371)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 14 06:33:20 PDT 2024


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/75371

>From cb64639669286e5f48421ae8f569208e1e9717be Mon Sep 17 00:00:00 2001
From: zoecarver <z.zoelec2 at gmail.com>
Date: Sat, 2 Dec 2023 20:00:30 +0000
Subject: [PATCH] [Builtin] Add __builtin_clear_padding

Adds `__builtin_clear_padding` to zero all padding bits of a struct. This builtin should match the behavior of those in NVCC and GCC (and MSVC?). There are some tests in this patch but hopefully we'll also get tests from other compilers (so all builtins can be as similar as possible).

I'm planning to add support for unions, bitfields (both as members and members of sub-objects), and booleans as follow up patches.

Differential Revision: https://reviews.llvm.org/D87974

overlapping subobjects + opague pointer

union, rename, scalar types
---
 clang/include/clang/Basic/Builtins.td         |   6 +
 clang/lib/CodeGen/CGBuiltin.cpp               | 207 +++++
 clang/lib/Sema/SemaChecking.cpp               |  31 +
 .../builtin-clear-padding-codegen.cpp         | 112 +++
 clang/test/SemaCXX/builtin-clear-padding.cpp  |  15 +
 .../atomics/builtin_clear_padding.pass.cpp    | 807 ++++++++++++++++++
 6 files changed, 1178 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp
 create mode 100644 clang/test/SemaCXX/builtin-clear-padding.cpp
 create mode 100644 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 11982af3fa609..44a239e50d5d8 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -932,6 +932,12 @@ def IsConstantEvaluated : LangBuiltin<"CXX_LANG"> {
   let Prototype = "bool()";
 }
 
+def ClearPadding : LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_clear_padding"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
 // GCC exception builtins
 def EHReturn : Builtin {
   let Spellings = ["__builtin_eh_return"];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index c16b69ba87567..e1d8135933bb3 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -63,6 +63,7 @@
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/TargetParser/AArch64TargetParser.h"
 #include "llvm/TargetParser/X86TargetParser.h"
+#include <algorithm>
 #include <optional>
 #include <sstream>
 
@@ -2538,6 +2539,205 @@ static RValue EmitHipStdParUnsupportedBuiltin(CodeGenFunction *CGF,
   return RValue::get(CGF->Builder.CreateCall(UBF, Args));
 }
 
+template <class T>
+void RecursivelyClearPaddingImpl(CodeGenFunction &CGF, Value *Ptr, QualType Ty,
+                                 size_t CurrentStartOffset,
+                                 size_t &RunningOffset, T &&WriteZeroAtOffset,
+                                 bool VisitVirtualBase);
+
+template <class T>
+void ClearPaddingStruct(CodeGenFunction &CGF, Value *Ptr, QualType Ty,
+                        StructType *ST, size_t CurrentStartOffset,
+                        size_t &RunningOffset, T &&WriteZeroAtOffset,
+                        bool VisitVirtualBase) {
+  llvm::dbgs() << "clear padding struct: " << ST->getName().data() << '\n';
+  const auto &DL = CGF.CGM.getModule().getDataLayout();
+  auto *SL = DL.getStructLayout(ST);
+  auto *R = dyn_cast<CXXRecordDecl>(Ty->getAsRecordDecl());
+  if (!R) {
+    llvm::dbgs() << "Not a CXXRecordDecl\n";
+    return;
+  }
+  const ASTRecordLayout &ASTLayout = CGF.getContext().getASTRecordLayout(R);
+  if (ASTLayout.hasOwnVFPtr()) {
+    llvm::dbgs() << "vtable ptr. Incrementing RunningOffset from "
+                 << RunningOffset << " to "
+                 << RunningOffset + DL.getPointerSizeInBits() / 8 << '\n';
+    RunningOffset += DL.getPointerSizeInBits() / 8;
+  }
+  std::vector<std::pair<size_t, CXXBaseSpecifier>> Bases;
+  Bases.reserve(R->getNumBases());
+  // todo get vbases
+  for (auto Base : R->bases()) {
+    auto *BaseRecord = cast<CXXRecordDecl>(Base.getType()->getAsRecordDecl());
+    if (!Base.isVirtual()) {
+      auto Offset = static_cast<size_t>(
+          ASTLayout.getBaseClassOffset(BaseRecord).getQuantity());
+      Bases.emplace_back(Offset, Base);
+    }
+  }
+
+  auto VisitBases =
+      [&](std::vector<std::pair<size_t, CXXBaseSpecifier>> &BasesToVisit) {
+        std::sort(
+            BasesToVisit.begin(), BasesToVisit.end(),
+            [](const auto &P1, const auto &P2) { return P1.first < P2.first; });
+        for (const auto &Pair : BasesToVisit) {
+          // is it OK to use structured binding in clang? what is the language
+          // version?
+          auto Offset = Pair.first;
+          auto Base = Pair.second;
+
+          llvm::dbgs() << "visiting base at offset " << Offset << '\n';
+          // 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, false);
+        }
+      };
+
+  VisitBases(Bases);
+
+  size_t NumFields = std::distance(R->field_begin(), R->field_end());
+  std::vector<size_t> FieldOffsets;
+  FieldOffsets.reserve(NumFields);
+  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;
+    llvm::dbgs() << "visiting field at offset " << Offset << '\n';
+    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, true);
+  }
+
+  if (VisitVirtualBase) {
+
+    std::vector<std::pair<size_t, CXXBaseSpecifier>> VBases;
+    VBases.reserve(R->getNumVBases());
+    for (auto VBase : R->vbases()) {
+      auto *BaseRecord =
+          cast<CXXRecordDecl>(VBase.getType()->getAsRecordDecl());
+      auto Offset = static_cast<size_t>(
+          ASTLayout.getVBaseClassOffset(BaseRecord).getQuantity());
+      VBases.emplace_back(Offset, VBase);
+    }
+
+    VisitBases(VBases);
+  }
+}
+
+template <class T>
+void ClearPaddingConstantArray(CodeGenFunction &CGF, Value *Ptr,
+                               llvm::Type *Type, ConstantArrayType const *AT,
+                               size_t CurrentStartOffset, size_t &RunningOffset,
+                               T &&WriteZeroAtOffset) {
+  llvm::dbgs() << "clear padding constant array\n";
+  for (size_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue();
+       ++ArrIndex) {
+
+    QualType ElementQualType = AT->getElementType();
+
+    auto *ElementRecord = ElementQualType->getAsRecordDecl();
+    if (!ElementRecord) {
+      llvm::dbgs() << "null!\n";
+    }
+    auto ElementAlign =
+        ElementRecord
+            ? CGF.getContext().getASTRecordLayout(ElementRecord).getAlignment()
+            : CGF.getContext().getTypeAlignInChars(ElementQualType);
+
+    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);
+    llvm::dbgs() << "clearing array index! " << ArrIndex << '\n';
+    RecursivelyClearPaddingImpl(CGF, Element.getBasePointer(), ElementQualType,
+                                CurrentStartOffset +
+                                    ArrIndex * AllocSize.getKnownMinValue(),
+                                RunningOffset, WriteZeroAtOffset, true);
+  }
+}
+
+template <class T>
+void RecursivelyClearPaddingImpl(CodeGenFunction &CGF, Value *Ptr, QualType Ty,
+                                 size_t CurrentStartOffset,
+                                 size_t &RunningOffset, T &&WriteZeroAtOffset,
+                                 bool VisitVirtualBase) {
+
+  llvm::dbgs() << "clear padding before current  [" << RunningOffset << ", "
+               << CurrentStartOffset << ")\n";
+  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<ConstantArrayType>(Ty)) {
+    ClearPaddingConstantArray(CGF, Ptr, Type, AT, CurrentStartOffset,
+                              RunningOffset, WriteZeroAtOffset);
+  } else if (auto *ST = dyn_cast<StructType>(Type); ST && Ty->isRecordType()) {
+    ClearPaddingStruct(CGF, Ptr, Ty, ST, CurrentStartOffset, RunningOffset,
+                       WriteZeroAtOffset, VisitVirtualBase);
+  } else if (Ty->isAtomicType()) {
+    RecursivelyClearPaddingImpl(CGF, Ptr, Ty.getAtomicUnqualifiedType(),
+                                CurrentStartOffset, RunningOffset,
+                                WriteZeroAtOffset, true);
+  } else {
+    llvm::dbgs() << "increment running offset from: " << RunningOffset << " to "
+                 << RunningOffset + Size << '\n';
+    RunningOffset =
+        std::max(RunningOffset, CurrentStartOffset + static_cast<size_t>(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 = [&](uint64_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,
+                              true);
+
+  // Clear tail padding
+  auto *Type = CGF.ConvertTypeForMem(Ty);
+
+  auto Size = CGF.CGM.getModule()
+                  .getDataLayout()
+                  .getTypeAllocSize(Type)
+                  .getKnownMinValue();
+
+  llvm::dbgs() << "clear tail padding  [" << RunningOffset << ", " << Size
+               << ")\n";
+  for (; RunningOffset < Size; ++RunningOffset) {
+    WriteZeroAtOffset(RunningOffset);
+  }
+}
+
 RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
                                         const CallExpr *E,
                                         ReturnValueSlot ReturnValue) {
@@ -4462,6 +4662,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 300af02239779..1915e64af0fad 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2655,6 +2655,37 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   }
   case Builtin::BI__builtin_launder:
     return BuiltinLaunder(*this, TheCall);
+  case Builtin::BI__builtin_clear_padding: {
+    const auto numArgs = TheCall->getNumArgs();
+    if (numArgs < 1) {
+      Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_one)
+          << 0 /*function call*/ << "T*" << 0;
+      return ExprError();
+    }
+    if (numArgs > 1) {
+      Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_many_args_one)
+          << 0 /*function call*/ << "T*" << numArgs << 0;
+      return ExprError();
+    }
+
+    const Expr *PtrArg = TheCall->getArg(0);
+    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/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'}}
+}
diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
new file mode 100644
index 0000000000000..d504ac58e43ae
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -0,0 +1,807 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <new>
+
+template <class T>
+void print_bytes(const T* object) {
+  auto size                        = sizeof(T);
+  const unsigned char* const bytes = reinterpret_cast<const unsigned char*>(object);
+  size_t i;
+
+  fprintf(stderr, "[ ");
+  for (i = 0; i < size; i++) {
+    fprintf(stderr, "%02x ", bytes[i]);
+  }
+  fprintf(stderr, "]\n");
+}
+
+template <class T>
+void __builtin_clear_padding2(T t) {
+  __builtin_clear_padding(t);
+}
+
+template <size_t A1, size_t A2, class T>
+struct alignas(A1) BasicWithPadding {
+  T x;
+  alignas(A2) T y;
+};
+
+template <size_t A1, size_t A2, size_t N, class T>
+struct alignas(A1) SpacedArrayMembers {
+  T x[N];
+  alignas(A2) char c;
+  T y[N];
+};
+
+template <size_t A1, size_t A2, class T>
+struct alignas(A1) PaddedPointerMembers {
+  T* x;
+  alignas(A2) T* y;
+};
+
+template <size_t A1, size_t A2, size_t A3, class T>
+struct alignas(A1) ThreeMembers {
+  T x;
+  alignas(A2) T y;
+  alignas(A3) T z;
+};
+
+template <class T>
+struct Normal {
+  T a;
+  T b;
+};
+
+template <class T>
+struct X {
+  T x;
+};
+
+template <class T>
+struct Z {
+  T z;
+};
+
+template <size_t A, class T>
+struct YZ : public Z<T> {
+  alignas(A) T y;
+};
+
+template <size_t A1, size_t A2, class T>
+struct alignas(A1) HasBase : public X<T>, public YZ<A2, T> {
+  T a;
+  alignas(A2) T b;
+};
+
+template <size_t A1, size_t A2, class T>
+void testAllStructsForType(T a, T b, T c, T d) {
+  // basic padding
+  {
+    using B = BasicWithPadding<A1, A2, T>;
+    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_padding2(&basic2);
+    assert(memcmp(&basic1, &basic2, sizeof(B)) == 0);
+  }
+
+  // spaced array
+  {
+    using A = SpacedArrayMembers<A1, A2, 2, T>;
+    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_padding2(&arr2);
+    assert(memcmp(&arr1, &arr2, sizeof(A)) == 0);
+  }
+
+  // pointer members
+  {
+    using P = PaddedPointerMembers<A1, A2, T>;
+    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_padding2(&ptr2);
+    assert(memcmp(&ptr1, &ptr2, sizeof(P)) == 0);
+  }
+
+  // three members
+  {
+    using Three = ThreeMembers<A1, A2, A2, T>;
+    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_padding2(&three2);
+    assert(memcmp(&three1, &three2, sizeof(Three)) == 0);
+  }
+
+  // Normal struct no padding
+  {
+    using N = Normal<T>;
+    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_padding2(&normal2);
+    assert(memcmp(&normal1, &normal2, sizeof(N)) == 0);
+  }
+
+  // base class
+  {
+    using H = HasBase<A1, A2, T>;
+    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_padding2(&base2);
+    assert(memcmp(&base1, &base2, sizeof(H)) == 0);
+  }
+}
+
+struct UnsizedTail {
+  int size;
+  alignas(8) char buf[];
+
+  UnsizedTail(int size) : size(size) {}
+};
+
+void otherStructTests() {
+  // Unsized Tail
+  {
+    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_padding2(u2);
+
+    assert(memcmp(u1, u2, sizeof(UnsizedTail)) == 0);
+  }
+
+  // basic padding on the heap
+  {
+    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_padding2(basic2);
+    assert(memcmp(basic1, basic2, sizeof(B)) == 0);
+    delete basic2;
+    delete basic1;
+  }
+
+  // basic padding volatile on the heap
+  {
+    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_padding2(const_cast<volatile B*>(basic4));
+    __builtin_clear_padding2(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_padding2(&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_padding2(&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_padding2(&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_padding2(&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_padding2(&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_padding2(&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_padding2(&s1);
+    assert(s1.c1 == 'a');
+    assert(s1.c2 == 'b');
+    assert(s1.c3 == 'c');
+    assert(memcmp(&s1, &s2, sizeof(S)) == 0);
+  }
+
+  // vtable
+  {
+    struct VirtualBase {
+      unsigned int x;
+      virtual int call() { return x; };
+      virtual ~VirtualBase() = default;
+    };
+
+    struct NonVirtualBase {
+      char y;
+    };
+
+    struct S : VirtualBase, NonVirtualBase {
+      virtual int call() override { return 5; }
+      bool z;
+    };
+
+    char buff1[sizeof(S)];
+    char buff2[sizeof(S)];
+    memset(buff1, 0, sizeof(S));
+    memset(buff2, 42, sizeof(S));
+
+    S* s1 = new (&buff1) S;
+    S* s2 = new (&buff2) S;
+
+    s1->x = 0xFFFFFFFF;
+    s2->x = 0xFFFFFFFF;
+    s1->y = 'a';
+    s2->y = 'a';
+    s1->z = true;
+    s2->z = true;
+    __builtin_clear_padding2(s2);
+    assert(s2->x == 0xFFFFFFFF);
+    assert(s2->y == 'a');
+    assert(s2->z == true);
+    assert(s2->call() == 5);
+    assert(memcmp(s1, s2, sizeof(S)) == 0);
+  }
+
+  // multiple bases with vtable
+  {
+    struct VirtualBase1 {
+      unsigned int x1;
+      virtual int call1() { return x1; };
+      virtual ~VirtualBase1() = default;
+    };
+
+    struct VirtualBase2 {
+      unsigned int x2;
+      virtual int call2() { return x2; };
+      virtual ~VirtualBase2() = default;
+    };
+
+    struct VirtualBase3 {
+      unsigned int x3;
+      virtual int call3() { return x3; };
+      virtual ~VirtualBase3() = default;
+    };
+
+    struct NonVirtualBase {
+      char y;
+    };
+
+    struct S : VirtualBase1, VirtualBase2, NonVirtualBase, VirtualBase3 {
+      virtual int call1() override { return 5; }
+      bool z;
+    };
+
+    char buff1[sizeof(S)];
+    char buff2[sizeof(S)];
+    memset(buff1, 0, sizeof(S));
+    memset(buff2, 42, sizeof(S));
+
+    S* s1 = new (&buff1) S;
+    S* s2 = new (&buff2) S;
+
+    s1->x1 = 0xFFFFFFFF;
+    s2->x1 = 0xFFFFFFFF;
+    s1->x2 = 0xFAFAFAFA;
+    s2->x2 = 0xFAFAFAFA;
+    s1->x3 = 0xAAAAAAAA;
+    s2->x3 = 0xAAAAAAAA;
+    s1->y  = 'a';
+    s2->y  = 'a';
+    s1->z  = true;
+    s2->z  = true;
+    __builtin_clear_padding2(s2);
+    assert(s2->x1 == 0xFFFFFFFF);
+    assert(s2->x2 == 0xFAFAFAFA);
+    assert(s2->x3 == 0xAAAAAAAA);
+    assert(s2->y == 'a');
+    assert(s2->z == true);
+    assert(s2->call1() == 5);
+    assert(memcmp(s1, s2, sizeof(S)) == 0);
+  }
+
+  // chain of bases with virtual functions
+  {
+    struct VirtualBase1 {
+      unsigned int x1;
+      virtual int call1() { return x1; };
+      virtual ~VirtualBase1() = default;
+    };
+
+    struct VirtualBase2 : VirtualBase1 {
+      unsigned int x2;
+      virtual int call2() { return x2; };
+      virtual ~VirtualBase2() = default;
+    };
+
+    struct VirtualBase3 : VirtualBase2 {
+      unsigned int x3;
+      virtual int call3() { return x3; };
+      virtual ~VirtualBase3() = default;
+    };
+
+    struct NonVirtualBase {
+      char y;
+    };
+
+    struct S : NonVirtualBase, VirtualBase3 {
+      //virtual int call() override { return 5; }
+      bool z;
+    };
+
+    char buff1[sizeof(S)];
+    char buff2[sizeof(S)];
+    memset(buff1, 0, sizeof(S));
+    memset(buff2, 42, sizeof(S));
+    S* s1 = new (&buff1) S;
+    S* s2 = new (&buff2) S;
+
+    s1->x1 = 0xFFFFFFFF;
+    s2->x1 = 0xFFFFFFFF;
+    s1->x2 = 0xFAFAFAFA;
+    s2->x2 = 0xFAFAFAFA;
+    s1->x3 = 0xAAAAAAAA;
+    s2->x3 = 0xAAAAAAAA;
+    s1->y  = 'a';
+    s2->y  = 'a';
+    s1->z  = true;
+    s2->z  = true;
+    __builtin_clear_padding2(s2);
+    assert(memcmp(s1, s2, sizeof(S)) == 0);
+  }
+
+  // virtual inheritance
+  {
+    struct Base {
+      int x;
+    };
+    struct D1 : virtual Base {
+      int d1;
+      bool b1;
+    };
+    struct D2 : virtual Base {
+      int d2;
+      bool b2;
+    };
+
+    struct S : D1, D2 {
+      bool s;
+    };
+
+    char buff1[sizeof(S)];
+    char buff2[sizeof(S)];
+    memset(buff1, 0, sizeof(S));
+    memset(buff2, 42, sizeof(S));
+    S* s1 = new (&buff1) S;
+    S* s2 = new (&buff2) S;
+
+    s1->x  = 0xFFFFFFFF;
+    s2->x  = 0xFFFFFFFF;
+    s1->d1 = 0xFAFAFAFA;
+    s2->d1 = 0xFAFAFAFA;
+    s1->d2 = 0xAAAAAAAA;
+    s2->d2 = 0xAAAAAAAA;
+    s1->b1 = true;
+    s2->b1 = true;
+    s1->b2 = true;
+    s2->b2 = true;
+    s1->s  = true;
+    s2->s  = true;
+    __builtin_clear_padding2(s2);
+    assert(memcmp(s1, s2, sizeof(S)) == 0);
+  }
+
+  // bit fields
+  {
+    struct S {
+      // will usually occupy 2 bytes:
+      unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1
+      unsigned char    : 2; // next 2 bits (in 1st byte) are blocked out as unused
+      unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd
+      unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte
+    };
+
+    S s1, s2;
+    memset(&s1, 0, sizeof(S));
+    memset(&s2, 42, sizeof(S));
+
+    s1.b1 = 5;
+    s2.b1 = 5;
+    s1.b2 = 27;
+    s2.b2 = 27;
+    s1.b3 = 3;
+    s2.b3 = 3;
+    __builtin_clear_padding(&s2);
+    print_bytes(&s1);
+    print_bytes(&s2);
+    //TODO
+    //assert(memcmp(&s1, &s2, sizeof(S)) == 0);
+  }
+
+  testAllStructsForType<32, 16, char>(11, 22, 33, 44);
+  testAllStructsForType<64, 32, char>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, volatile char>(11, 22, 33, 44);
+  testAllStructsForType<64, 32, volatile char>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, int>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, int>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, volatile int>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, volatile int>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, double>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, double>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, _BitInt(28)>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, _BitInt(28)>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, _BitInt(60)>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, _BitInt(60)>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, _BitInt(64)>(0, 1, 2, 3);
+  testAllStructsForType<64, 32, _BitInt(64)>(4, 5, 6, 7);
+  testAllStructsForType<32, 16, Foo>(Foo{1, 2}, Foo{3, 4}, Foo{1, 2}, Foo{3, 4});
+  testAllStructsForType<64, 32, Foo>(Foo{1, 2}, Foo{3, 4}, Foo{1, 2}, Foo{3, 4});
+  testAllStructsForType<256, 128, Float3Vec>(0, 1, 2, 3);
+  testAllStructsForType<128, 128, Float3Vec>(4, 5, 6, 7);
+  testAllStructsForType<256, 128, Float4Vec>(0, 1, 2, 3);
+  testAllStructsForType<128, 128, Float4Vec>(4, 5, 6, 7);
+
+  otherStructTests();
+}
+
+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_padding2(&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_padding2(&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_padding2(&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_padding2(&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_padding2(&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;
+}



More information about the libcxx-commits mailing list