[clang] c4f95ef - Reimplement `__builtin_dump_struct` in Sema.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu May 5 14:55:56 PDT 2022
Author: Richard Smith
Date: 2022-05-05T14:55:47-07:00
New Revision: c4f95ef86a224fe730d2219aab90e88a0e7b03d2
URL: https://github.com/llvm/llvm-project/commit/c4f95ef86a224fe730d2219aab90e88a0e7b03d2
DIFF: https://github.com/llvm/llvm-project/commit/c4f95ef86a224fe730d2219aab90e88a0e7b03d2.diff
LOG: Reimplement `__builtin_dump_struct` in Sema.
Compared to the old implementation:
* In C++, we only recurse into aggregate classes.
* Unnamed bit-fields are not printed.
* Constant evaluation is supported.
* Proper conversion is done when passing arguments through `...`.
* Additional arguments are supported and are injected prior to the
format string; this directly supports use with `fprintf`, for example.
* An arbitrary callable can be passed rather than only a function
pointer. In particular, in C++, a function template or overload set is
acceptable.
* All text generated by Clang is printed via `%s` rather than directly;
this avoids issues where Clang's pretty-printing output might itself
contain a `%` character.
* Fields of types that we don't know how to print are printed with a
`"*%p"` format and passed by address to the print function.
* No return value is produced.
Reviewed By: aaron.ballman, erichkeane, yihanaa
Differential Revision: https://reviews.llvm.org/D124221
Added:
clang/test/CodeGen/builtin-dump-struct.c
clang/test/CodeGenCXX/builtin-dump-struct.cpp
clang/test/SemaCXX/builtin-dump-struct.cpp
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Builtins.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Sema/SemaChecking.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/test/Sema/builtin-dump-struct.c
Removed:
clang/test/CodeGen/dump-struct-builtin.c
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 426ca4aa24dc..bc90f9cf7480 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2373,44 +2373,82 @@ controlled state.
.. code-block:: c++
- __builtin_dump_struct(&some_struct, &some_printf_func);
+ __builtin_dump_struct(&some_struct, some_printf_func, args...);
**Examples**:
.. code-block:: c++
- struct S {
- int x, y;
- float f;
- struct T {
- int i;
- } t;
- };
+ struct S {
+ int x, y;
+ float f;
+ struct T {
+ int i;
+ } t;
+ };
- void func(struct S *s) {
- __builtin_dump_struct(s, &printf);
- }
+ void func(struct S *s) {
+ __builtin_dump_struct(s, printf);
+ }
Example output:
.. code-block:: none
- struct S {
- int i : 100
- int j : 42
- float f : 3.14159
- struct T t : struct T {
- int i : 1997
- }
- }
+ struct S {
+ int x = 100
+ int y = 42
+ float f = 3.141593
+ struct T t = {
+ int i = 1997
+ }
+ }
+
+.. code-block:: c++
+
+ #include <string>
+ struct T { int a, b; };
+ constexpr void constexpr_sprintf(std::string &out, const char *format,
+ auto ...args) {
+ // ...
+ }
+ constexpr std::string dump_struct(auto &x) {
+ std::string s;
+ __builtin_dump_struct(&x, constexpr_sprintf, s);
+ return s;
+ }
+ static_assert(dump_struct(T{1, 2}) == R"(struct T {
+ int a = 1
+ int b = 2
+ }
+ )");
**Description**:
-The '``__builtin_dump_struct``' function is used to print the fields of a simple
-structure and their values for debugging purposes. The builtin accepts a pointer
-to a structure to dump the fields of, and a pointer to a formatted output
-function whose signature must be: ``int (*)(const char *, ...)`` and must
-support the format specifiers used by ``printf()``.
+The ``__builtin_dump_struct`` function is used to print the fields of a simple
+structure and their values for debugging purposes. The first argument of the
+builtin should be a pointer to the struct to dump. The second argument ``f``
+should be some callable expression, and can be a function object or an overload
+set. The builtin calls ``f``, passing any further arguments ``args...``
+followed by a ``printf``-compatible format string and the corresponding
+arguments. ``f`` may be called more than once, and ``f`` and ``args`` will be
+evaluated once per call. In C++, ``f`` may be a template or overload set and
+resolve to
diff erent functions for each call.
+
+In the format string, a suitable format specifier will be used for builtin
+types that Clang knows how to format. This includes standard builtin types, as
+well as aggregate structures, ``void*`` (printed with ``%p``), and ``const
+char*`` (printed with ``%s``). A ``*%p`` specifier will be used for a field
+that Clang doesn't know how to format, and the corresopnding argument will be a
+pointer to the field. This allows a C++ templated formatting function to detect
+this case and implement custom formatting. A ``*`` will otherwise not precede a
+format specifier.
+
+This builtin does not return a value.
+
+This builtin can be used in constant expressions.
+
+Query for this feature with ``__has_builtin(__builtin_dump_struct)``
.. _langext-__builtin_shufflevector:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 090b4c6b1b6c..47abde0747d6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -211,8 +211,15 @@ Non-comprehensive list of changes in this release
- Improve __builtin_dump_struct:
- Support bitfields in struct and union.
- Improve the dump format, dump both bitwidth(if its a bitfield) and field value.
- - Remove anonymous tag locations.
- - Beautify dump format, add indent for nested struct and struct members.
+ - Remove anonymous tag locations and flatten anonymous struct members.
+ - Beautify dump format, add indent for struct members.
+ - Support passing additional arguments to the formatting function, allowing
+ use with ``fprintf`` and similar formatting functions.
+ - Support use within constant evaluation in C++, if a ``constexpr``
+ formatting function is provided.
+ - Support formatting of base classes in C++.
+ - Support calling a formatting function template in C++, which can provide
+ custom formatting for non-aggregate types.
- Previously disabled sanitizer options now enabled by default:
- ASAN_OPTIONS=detect_stack_use_after_return=1 (only on Linux).
- MSAN_OPTIONS=poison_in_dtor=1.
diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index e86475b0e260..ad55fdbc7c62 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -1603,7 +1603,7 @@ BUILTIN(__builtin_function_start, "v*v&", "nct")
BUILTIN(__builtin_operator_new, "v*z", "tc")
BUILTIN(__builtin_operator_delete, "vv*", "tn")
BUILTIN(__builtin_char_memchr, "c*cC*iz", "n")
-BUILTIN(__builtin_dump_struct, "ivC*v*", "tn")
+BUILTIN(__builtin_dump_struct, "v.", "t")
BUILTIN(__builtin_preserve_access_index, "v.", "t")
// Alignment builtins (uses custom parsing to support pointers and integers)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 614bacc34af5..2e1688d798f2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8471,6 +8471,12 @@ def err_overflow_builtin_must_be_ptr_int : Error<
def err_overflow_builtin_bit_int_max_size : Error<
"__builtin_mul_overflow does not support 'signed _BitInt' operands of more "
"than %0 bits">;
+def err_expected_struct_pointer_argument : Error<
+ "expected pointer to struct as %ordinal0 argument to %1, found %2">;
+def err_expected_callable_argument : Error<
+ "expected a callable expression as %ordinal0 argument to %1, found %2">;
+def note_building_builtin_dump_struct_call : Note<
+ "in call to printing function with arguments '(%0)' while dumping struct">;
def err_atomic_load_store_uses_lib : Error<
"atomic %select{load|store}0 requires runtime support that is not "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7d33b5047a67..44fbffdda390 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8897,6 +8897,10 @@ class Sema final {
/// We are marking a class as __dllexport.
MarkingClassDllexported,
+ /// We are building an implied call from __builtin_dump_struct. The
+ /// arguments are in CallArgs.
+ BuildingBuiltinDumpStructCall,
+
/// Added for Template instantiation observation.
/// Memoization means we are _not_ instantiating a template because
/// it is already instantiated (but we entered a context where we
@@ -8918,9 +8922,14 @@ class Sema final {
/// arguments.
NamedDecl *Template;
- /// The list of template arguments we are substituting, if they
- /// are not part of the entity.
- const TemplateArgument *TemplateArgs;
+ union {
+ /// The list of template arguments we are substituting, if they
+ /// are not part of the entity.
+ const TemplateArgument *TemplateArgs;
+
+ /// The list of argument expressions in a synthesized call.
+ const Expr *const *CallArgs;
+ };
// FIXME: Wrap this union around more members, or perhaps store the
// kind-specific members in the RAII object owning the context.
@@ -8928,6 +8937,9 @@ class Sema final {
/// The number of template arguments in TemplateArgs.
unsigned NumTemplateArgs;
+ /// The number of expressions in CallArgs.
+ unsigned NumCallArgs;
+
/// The special member being declared or defined.
CXXSpecialMember SpecialMember;
};
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index caea5d16b265..a76677ecc2b2 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -24,7 +24,6 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/OSLog.h"
-#include "clang/AST/FormatString.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
@@ -2044,117 +2043,6 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1,
return RValue::get(Overflow);
}
-static std::string getPrintfSpecifier(CodeGenFunction &CGF, QualType QT) {
- analyze_printf::PrintfSpecifier spec;
- if (!spec.fixType(QT, CGF.getLangOpts(), CGF.getContext(), false)) {
- // If this type is a boolean type, we should use '%d' to dump its value.
- if (QT->isBooleanType())
- return "%d";
-
- // Otherwise, in order to keep the same behavior as before, use '%p' for
- // unknown types
- return "%p";
- }
- std::string str;
- llvm::raw_string_ostream ss(str);
- spec.toString(ss);
- return str;
-}
-
-static llvm::Value *dumpValue(CodeGenFunction &CGF, QualType RType,
- LValue RecordLV, CharUnits Align,
- llvm::FunctionCallee Func, PrintingPolicy Policy,
- int Lvl) {
- RecordDecl *RD = RType->castAs<RecordType>()->getDecl()->getDefinition();
- std::string Pad = std::string(Lvl * 4, ' ');
- std::string ElementPad = std::string((Lvl + 1) * 4, ' ');
-
- Value *GString = CGF.Builder.CreateGlobalStringPtr("{\n");
- Value *Res = CGF.Builder.CreateCall(Func, {GString});
-
- for (const auto *FD : RD->fields()) {
- Value *TmpRes = nullptr;
-
- std::string Format = llvm::Twine(ElementPad)
- .concat(FD->getType().getAsString(Policy))
- .concat(llvm::Twine(' '))
- .concat(FD->getNameAsString())
- .str();
-
- if (FD->isBitField()) {
- unsigned BitfieldWidth = FD->getBitWidthValue(CGF.getContext());
-
- // If current field is a unnamed bitfield, we should dump only one ' '
- // between type-name and ':'
- if (!FD->getDeclName().isEmpty())
- Format += ' ';
- Format += llvm::Twine(": ").concat(llvm::Twine(BitfieldWidth)).str();
-
- // If current field is a zero-width bitfield, we just dump a string like
- // 'type-name : 0'
- if (FD->isZeroSize(CGF.getContext())) {
- Format += "\n";
- GString = CGF.Builder.CreateGlobalStringPtr(Format);
- TmpRes = CGF.Builder.CreateCall(Func, {GString});
- Res = CGF.Builder.CreateAdd(Res, TmpRes);
- continue;
- }
- }
-
- GString = CGF.Builder.CreateGlobalStringPtr(
- llvm::Twine(Format).concat(" = ").str());
- TmpRes = CGF.Builder.CreateCall(Func, {GString});
- Res = CGF.Builder.CreateAdd(TmpRes, Res);
-
- LValue FieldLV = CGF.EmitLValueForField(RecordLV, FD);
- QualType CanonicalType =
- FD->getType().getUnqualifiedType().getCanonicalType();
-
- // We check whether we are in a recursive type
- if (CanonicalType->isRecordType()) {
-
- // If current field is a record type, we should not dump the type name in
- // recursive dumpRecord call, and we only dump the things between {...}
- TmpRes =
- dumpValue(CGF, CanonicalType, FieldLV, Align, Func, Policy, Lvl + 1);
- Res = CGF.Builder.CreateAdd(TmpRes, Res);
- continue;
- }
-
- // We try to determine the best format to print the current field
- std::string PrintFormatSpec = getPrintfSpecifier(CGF, FD->getType());
- GString = CGF.Builder.CreateGlobalStringPtr(
- llvm::Twine(PrintFormatSpec).concat(llvm::Twine('\n')).str());
-
- RValue RV = FD->isBitField()
- ? CGF.EmitLoadOfBitfieldLValue(FieldLV, FD->getLocation())
- : CGF.EmitLoadOfLValue(FieldLV, FD->getLocation());
-
- /// FIXME: This place needs type promotion.
- TmpRes = CGF.Builder.CreateCall(Func, {GString, RV.getScalarVal()});
- Res = CGF.Builder.CreateAdd(Res, TmpRes);
- }
-
- GString = CGF.Builder.CreateGlobalStringPtr(Pad + "}\n");
- Value *TmpRes = CGF.Builder.CreateCall(Func, {GString});
- Res = CGF.Builder.CreateAdd(Res, TmpRes);
- return Res;
-}
-
-static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType,
- LValue RecordLV, CharUnits Align,
- llvm::FunctionCallee Func) {
- ASTContext &Context = CGF.getContext();
- PrintingPolicy Policy(Context.getLangOpts());
- Policy.AnonymousTagLocations = false;
- std::string Name = llvm::Twine(RType.getAsString(Policy)).concat(" ").str();
- Value *GString = CGF.Builder.CreateGlobalStringPtr(Name);
- Value *Res = CGF.Builder.CreateCall(Func, {GString});
- Value *TmpRes = dumpValue(CGF, RType, RecordLV, Align, Func, Policy, 0);
- Res = CGF.Builder.CreateAdd(Res, TmpRes);
- return Res;
-}
-
static bool
TypeRequiresBuiltinLaunderImp(const ASTContext &Ctx, QualType Ty,
llvm::SmallPtrSetImpl<const Decl *> &Seen) {
@@ -2681,24 +2569,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(ComplexVal.first);
}
- case Builtin::BI__builtin_dump_struct: {
- llvm::Type *LLVMIntTy = getTypes().ConvertType(getContext().IntTy);
- llvm::FunctionType *LLVMFuncType = llvm::FunctionType::get(
- LLVMIntTy, {llvm::Type::getInt8PtrTy(getLLVMContext())}, true);
-
- Value *Func = EmitScalarExpr(E->getArg(1)->IgnoreImpCasts());
- CharUnits Arg0Align = EmitPointerWithAlignment(E->getArg(0)).getAlignment();
-
- const Expr *Arg0 = E->getArg(0)->IgnoreImpCasts();
- QualType Arg0Type = Arg0->getType()->getPointeeType();
-
- Value *RecordPtr = EmitScalarExpr(Arg0);
- LValue RecordLV = MakeAddrLValue(RecordPtr, Arg0Type, Arg0Align);
- Value *Res = dumpRecord(*this, Arg0Type, RecordLV, Arg0Align,
- {LLVMFuncType, Func});
- return RValue::get(Res);
- }
-
case Builtin::BI__builtin_preserve_access_index: {
// Only enabled preserved access index region when debuginfo
// is available as debuginfo is needed to preserve user-level
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e219be35cfea..10d6e1f8417b 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -480,6 +480,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "InitializingStructuredBinding";
case CodeSynthesisContext::MarkingClassDllexported:
return "MarkingClassDllexported";
+ case CodeSynthesisContext::BuildingBuiltinDumpStructCall:
+ return "BuildingBuiltinDumpStructCall";
}
return "";
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 18387c241185..1ef8a6ae833a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -109,24 +109,38 @@ SourceLocation Sema::getLocationOfStringLiteralByte(const StringLiteral *SL,
Context.getTargetInfo());
}
+/// Checks that a call expression's argument count is at least the desired
+/// number. This is useful when doing custom type-checking on a variadic
+/// function. Returns true on error.
+static bool checkArgCountAtLeast(Sema &S, CallExpr *Call,
+ unsigned MinArgCount) {
+ unsigned ArgCount = Call->getNumArgs();
+ if (ArgCount >= MinArgCount)
+ return false;
+
+ return S.Diag(Call->getEndLoc(), diag::err_typecheck_call_too_few_args)
+ << 0 /*function call*/ << MinArgCount << ArgCount
+ << Call->getSourceRange();
+}
+
/// Checks that a call expression's argument count is the desired number.
/// This is useful when doing custom type-checking. Returns true on error.
-static bool checkArgCount(Sema &S, CallExpr *call, unsigned desiredArgCount) {
- unsigned argCount = call->getNumArgs();
- if (argCount == desiredArgCount) return false;
+static bool checkArgCount(Sema &S, CallExpr *Call, unsigned DesiredArgCount) {
+ unsigned ArgCount = Call->getNumArgs();
+ if (ArgCount == DesiredArgCount)
+ return false;
- if (argCount < desiredArgCount)
- return S.Diag(call->getEndLoc(), diag::err_typecheck_call_too_few_args)
- << 0 /*function call*/ << desiredArgCount << argCount
- << call->getSourceRange();
+ if (checkArgCountAtLeast(S, Call, DesiredArgCount))
+ return true;
+ assert(ArgCount > DesiredArgCount && "should have diagnosed this");
// Highlight all the excess arguments.
- SourceRange range(call->getArg(desiredArgCount)->getBeginLoc(),
- call->getArg(argCount - 1)->getEndLoc());
+ SourceRange Range(Call->getArg(DesiredArgCount)->getBeginLoc(),
+ Call->getArg(ArgCount - 1)->getEndLoc());
- return S.Diag(range.getBegin(), diag::err_typecheck_call_too_many_args)
- << 0 /*function call*/ << desiredArgCount << argCount
- << call->getArg(1)->getSourceRange();
+ return S.Diag(Range.getBegin(), diag::err_typecheck_call_too_many_args)
+ << 0 /*function call*/ << DesiredArgCount << ArgCount
+ << Call->getArg(1)->getSourceRange();
}
/// Check that the first argument to __builtin_annotation is an integer
@@ -366,6 +380,311 @@ static bool SemaBuiltinOverflow(Sema &S, CallExpr *TheCall,
return false;
}
+namespace {
+struct BuiltinDumpStructGenerator {
+ Sema &S;
+ CallExpr *TheCall;
+ SourceLocation Loc = TheCall->getBeginLoc();
+ SmallVector<Expr *, 32> Actions;
+ DiagnosticErrorTrap ErrorTracker;
+ PrintingPolicy Policy;
+
+ BuiltinDumpStructGenerator(Sema &S, CallExpr *TheCall)
+ : S(S), TheCall(TheCall), ErrorTracker(S.getDiagnostics()),
+ Policy(S.Context.getPrintingPolicy()) {
+ Policy.AnonymousTagLocations = false;
+ }
+
+ Expr *makeOpaqueValueExpr(Expr *Inner) {
+ auto *OVE = new (S.Context)
+ OpaqueValueExpr(Loc, Inner->getType(), Inner->getValueKind(),
+ Inner->getObjectKind(), Inner);
+ Actions.push_back(OVE);
+ return OVE;
+ }
+
+ Expr *getStringLiteral(llvm::StringRef Str) {
+ Expr *Lit = S.Context.getPredefinedStringLiteralFromCache(Str);
+ // Wrap the literal in parentheses to attach a source location.
+ return new (S.Context) ParenExpr(Loc, Loc, Lit);
+ }
+
+ bool callPrintFunction(llvm::StringRef Format,
+ llvm::ArrayRef<Expr *> Exprs = {}) {
+ SmallVector<Expr *, 8> Args;
+ assert(TheCall->getNumArgs() >= 2);
+ Args.reserve((TheCall->getNumArgs() - 2) + /*Format*/ 1 + Exprs.size());
+ Args.assign(TheCall->arg_begin() + 2, TheCall->arg_end());
+ Args.push_back(getStringLiteral(Format));
+ Args.insert(Args.end(), Exprs.begin(), Exprs.end());
+
+ // Register a note to explain why we're performing the call.
+ Sema::CodeSynthesisContext Ctx;
+ Ctx.Kind = Sema::CodeSynthesisContext::BuildingBuiltinDumpStructCall;
+ Ctx.PointOfInstantiation = Loc;
+ Ctx.CallArgs = Args.data();
+ Ctx.NumCallArgs = Args.size();
+ S.pushCodeSynthesisContext(Ctx);
+
+ ExprResult RealCall =
+ S.BuildCallExpr(/*Scope=*/nullptr, TheCall->getArg(1),
+ TheCall->getBeginLoc(), Args, TheCall->getRParenLoc());
+
+ S.popCodeSynthesisContext();
+ if (!RealCall.isInvalid())
+ Actions.push_back(RealCall.get());
+ // Bail out if we've hit any errors, even if we managed to build the
+ // call. We don't want to produce more than one error.
+ return RealCall.isInvalid() || ErrorTracker.hasErrorOccurred();
+ }
+
+ Expr *getIndentString(unsigned Depth) {
+ if (!Depth)
+ return nullptr;
+
+ llvm::SmallString<32> Indent;
+ Indent.resize(Depth * Policy.Indentation, ' ');
+ return getStringLiteral(Indent);
+ }
+
+ Expr *getTypeString(QualType T) {
+ return getStringLiteral(T.getAsString(Policy));
+ }
+
+ bool appendFormatSpecifier(QualType T, llvm::SmallVectorImpl<char> &Str) {
+ llvm::raw_svector_ostream OS(Str);
+
+ // Format 'bool', 'char', 'signed char', 'unsigned char' as numbers, rather
+ // than trying to print a single character.
+ if (auto *BT = T->getAs<BuiltinType>()) {
+ switch (BT->getKind()) {
+ case BuiltinType::Bool:
+ OS << "%d";
+ return true;
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ OS << "%hhu";
+ return true;
+ case BuiltinType::Char_S:
+ case BuiltinType::SChar:
+ OS << "%hhd";
+ return true;
+ default:
+ break;
+ }
+ }
+
+ analyze_printf::PrintfSpecifier Specifier;
+ if (Specifier.fixType(T, S.getLangOpts(), S.Context, /*IsObjCLiteral=*/false)) {
+ // We were able to guess how to format this.
+ if (Specifier.getConversionSpecifier().getKind() ==
+ analyze_printf::PrintfConversionSpecifier::sArg) {
+ // Wrap double-quotes around a '%s' specifier and limit its maximum
+ // length. Ideally we'd also somehow escape special characters in the
+ // contents but printf doesn't support that.
+ // FIXME: '%s' formatting is not safe in general.
+ OS << '"';
+ Specifier.setPrecision(analyze_printf::OptionalAmount(32u));
+ Specifier.toString(OS);
+ OS << '"';
+ // FIXME: It would be nice to include a '...' if the string doesn't fit
+ // in the length limit.
+ } else {
+ Specifier.toString(OS);
+ }
+ return true;
+ }
+
+ if (T->isPointerType()) {
+ // Format all pointers with '%p'.
+ OS << "%p";
+ return true;
+ }
+
+ return false;
+ }
+
+ bool dumpUnnamedRecord(const RecordDecl *RD, Expr *E, unsigned Depth) {
+ Expr *IndentLit = getIndentString(Depth);
+ Expr *TypeLit = getTypeString(S.Context.getRecordType(RD));
+ if (IndentLit ? callPrintFunction("%s%s", {IndentLit, TypeLit})
+ : callPrintFunction("%s", {TypeLit}))
+ return true;
+
+ return dumpRecordValue(RD, E, IndentLit, Depth);
+ }
+
+ // Dump a record value. E should be a pointer or lvalue referring to an RD.
+ bool dumpRecordValue(const RecordDecl *RD, Expr *E, Expr *RecordIndent,
+ unsigned Depth) {
+ // FIXME: Decide what to do if RD is a union. At least we should probably
+ // turn off printing `const char*` members with `%s`, because that is very
+ // likely to crash if that's not the active member. Whatever we decide, we
+ // should document it.
+
+ // Build an OpaqueValueExpr so we can refer to E more than once without
+ // triggering re-evaluation.
+ Expr *RecordArg = makeOpaqueValueExpr(E);
+ bool RecordArgIsPtr = RecordArg->getType()->isPointerType();
+
+ if (callPrintFunction(" {\n"))
+ return true;
+
+ // Dump each base class, regardless of whether they're aggregates.
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const auto &Base : CXXRD->bases()) {
+ QualType BaseType =
+ RecordArgIsPtr ? S.Context.getPointerType(Base.getType())
+ : S.Context.getLValueReferenceType(Base.getType());
+ ExprResult BasePtr = S.BuildCStyleCastExpr(
+ Loc, S.Context.getTrivialTypeSourceInfo(BaseType, Loc), Loc,
+ RecordArg);
+ if (BasePtr.isInvalid() ||
+ dumpUnnamedRecord(Base.getType()->getAsRecordDecl(), BasePtr.get(),
+ Depth + 1))
+ return true;
+ }
+ }
+
+ Expr *FieldIndentArg = getIndentString(Depth + 1);
+
+ // Dump each field.
+ for (auto *D : RD->decls()) {
+ auto *IFD = dyn_cast<IndirectFieldDecl>(D);
+ auto *FD = IFD ? IFD->getAnonField() : dyn_cast<FieldDecl>(D);
+ if (!FD || FD->isUnnamedBitfield() || FD->isAnonymousStructOrUnion())
+ continue;
+
+ llvm::SmallString<20> Format = llvm::StringRef("%s%s %s ");
+ llvm::SmallVector<Expr *, 5> Args = {FieldIndentArg,
+ getTypeString(FD->getType()),
+ getStringLiteral(FD->getName())};
+
+ if (FD->isBitField()) {
+ Format += ": %zu ";
+ QualType SizeT = S.Context.getSizeType();
+ llvm::APInt BitWidth(S.Context.getIntWidth(SizeT),
+ FD->getBitWidthValue(S.Context));
+ Args.push_back(IntegerLiteral::Create(S.Context, BitWidth, SizeT, Loc));
+ }
+
+ Format += "=";
+
+ ExprResult Field =
+ IFD ? S.BuildAnonymousStructUnionMemberReference(
+ CXXScopeSpec(), Loc, IFD,
+ DeclAccessPair::make(IFD, AS_public), RecordArg, Loc)
+ : S.BuildFieldReferenceExpr(
+ RecordArg, RecordArgIsPtr, Loc, CXXScopeSpec(), FD,
+ DeclAccessPair::make(FD, AS_public),
+ DeclarationNameInfo(FD->getDeclName(), Loc));
+ if (Field.isInvalid())
+ return true;
+
+ auto *InnerRD = FD->getType()->getAsRecordDecl();
+ auto *InnerCXXRD = dyn_cast_or_null<CXXRecordDecl>(InnerRD);
+ if (InnerRD && (!InnerCXXRD || InnerCXXRD->isAggregate())) {
+ // Recursively print the values of members of aggregate record type.
+ if (callPrintFunction(Format, Args) ||
+ dumpRecordValue(InnerRD, Field.get(), FieldIndentArg, Depth + 1))
+ return true;
+ } else {
+ Format += " ";
+ if (appendFormatSpecifier(FD->getType(), Format)) {
+ // We know how to print this field.
+ Args.push_back(Field.get());
+ } else {
+ // We don't know how to print this field. Print out its address
+ // with a format specifier that a smart tool will be able to
+ // recognize and treat specially.
+ Format += "*%p";
+ ExprResult FieldAddr =
+ S.BuildUnaryOp(nullptr, Loc, UO_AddrOf, Field.get());
+ if (FieldAddr.isInvalid())
+ return true;
+ Args.push_back(FieldAddr.get());
+ }
+ Format += "\n";
+ if (callPrintFunction(Format, Args))
+ return true;
+ }
+ }
+
+ return RecordIndent ? callPrintFunction("%s}\n", RecordIndent)
+ : callPrintFunction("}\n");
+ }
+
+ Expr *buildWrapper() {
+ auto *Wrapper = PseudoObjectExpr::Create(S.Context, TheCall, Actions,
+ PseudoObjectExpr::NoResult);
+ TheCall->setType(Wrapper->getType());
+ TheCall->setValueKind(Wrapper->getValueKind());
+ return Wrapper;
+ }
+};
+} // namespace
+
+static ExprResult SemaBuiltinDumpStruct(Sema &S, CallExpr *TheCall) {
+ if (checkArgCountAtLeast(S, TheCall, 2))
+ return ExprError();
+
+ ExprResult PtrArgResult = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (PtrArgResult.isInvalid())
+ return ExprError();
+ TheCall->setArg(0, PtrArgResult.get());
+
+ // First argument should be a pointer to a struct.
+ QualType PtrArgType = PtrArgResult.get()->getType();
+ if (!PtrArgType->isPointerType() ||
+ !PtrArgType->getPointeeType()->isRecordType()) {
+ S.Diag(PtrArgResult.get()->getBeginLoc(),
+ diag::err_expected_struct_pointer_argument)
+ << 1 << TheCall->getDirectCallee() << PtrArgType;
+ return ExprError();
+ }
+ const RecordDecl *RD = PtrArgType->getPointeeType()->getAsRecordDecl();
+
+ // Second argument is a callable, but we can't fully validate it until we try
+ // calling it.
+ QualType FnArgType = TheCall->getArg(1)->getType();
+ if (!FnArgType->isFunctionType() && !FnArgType->isFunctionPointerType() &&
+ !FnArgType->isBlockPointerType() &&
+ !(S.getLangOpts().CPlusPlus && FnArgType->isRecordType())) {
+ auto *BT = FnArgType->getAs<BuiltinType>();
+ switch (BT ? BT->getKind() : BuiltinType::Void) {
+ case BuiltinType::Dependent:
+ case BuiltinType::Overload:
+ case BuiltinType::BoundMember:
+ case BuiltinType::PseudoObject:
+ case BuiltinType::UnknownAny:
+ case BuiltinType::BuiltinFn:
+ // This might be a callable.
+ break;
+
+ default:
+ S.Diag(TheCall->getArg(1)->getBeginLoc(),
+ diag::err_expected_callable_argument)
+ << 2 << TheCall->getDirectCallee() << FnArgType;
+ return ExprError();
+ }
+ }
+
+ BuiltinDumpStructGenerator Generator(S, TheCall);
+
+ // Wrap parentheses around the given pointer. This is not necessary for
+ // correct code generation, but it means that when we pretty-print the call
+ // arguments in our diagnostics we will produce '(&s)->n' instead of the
+ // incorrect '&s->n'.
+ Expr *PtrArg = PtrArgResult.get();
+ PtrArg = new (S.Context)
+ ParenExpr(PtrArg->getBeginLoc(),
+ S.getLocForEndOfToken(PtrArg->getEndLoc()), PtrArg);
+ if (Generator.dumpUnnamedRecord(RD, PtrArg, 0))
+ return ExprError();
+
+ return Generator.buildWrapper();
+}
+
static bool SemaBuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) {
if (checkArgCount(S, BuiltinCall, 2))
return true;
@@ -2013,62 +2332,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CorrectDelayedTyposInExpr(TheCallResult.get());
return Res;
}
- case Builtin::BI__builtin_dump_struct: {
- // We first want to ensure we are called with 2 arguments
- if (checkArgCount(*this, TheCall, 2))
- return ExprError();
- // Ensure that the first argument is of type 'struct XX *'
- const Expr *PtrArg = TheCall->getArg(0)->IgnoreParenImpCasts();
- const QualType PtrArgType = PtrArg->getType();
- if (!PtrArgType->isPointerType() ||
- !PtrArgType->getPointeeType()->isRecordType()) {
- Diag(PtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << PtrArgType << "structure pointer" << 1 << 0 << 3 << 1 << PtrArgType
- << "structure pointer";
- return ExprError();
- }
-
- // Ensure that the second argument is of type 'FunctionType'
- const Expr *FnPtrArg = TheCall->getArg(1)->IgnoreImpCasts();
- const QualType FnPtrArgType = FnPtrArg->getType();
- if (!FnPtrArgType->isPointerType()) {
- Diag(FnPtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3 << 2
- << FnPtrArgType << "'int (*)(const char *, ...)'";
- return ExprError();
- }
-
- const auto *FuncType =
- FnPtrArgType->getPointeeType()->getAs<FunctionType>();
-
- if (!FuncType) {
- Diag(FnPtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3 << 2
- << FnPtrArgType << "'int (*)(const char *, ...)'";
- return ExprError();
- }
-
- if (const auto *FT = dyn_cast<FunctionProtoType>(FuncType)) {
- if (!FT->getNumParams()) {
- Diag(FnPtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3
- << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";
- return ExprError();
- }
- QualType PT = FT->getParamType(0);
- if (!FT->isVariadic() || FT->getReturnType() != Context.IntTy ||
- !PT->isPointerType() || !PT->getPointeeType()->isCharType() ||
- !PT->getPointeeType().isConstQualified()) {
- Diag(FnPtrArg->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3
- << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";
- return ExprError();
- }
- }
-
- TheCall->setType(Context.IntTy);
- break;
- }
+ case Builtin::BI__builtin_dump_struct:
+ return SemaBuiltinDumpStruct(*this, TheCall);
case Builtin::BI__builtin_expect_with_probability: {
// We first want to ensure we are called with 3 arguments
if (checkArgCount(*this, TheCall, 3))
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 1143023bb5ba..4fd9d003e66a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -213,6 +213,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case RewritingOperatorAsSpaceship:
case InitializingStructuredBinding:
case MarkingClassDllexported:
+ case BuildingBuiltinDumpStructCall:
return false;
// This function should never be called when Kind's value is Memoization.
@@ -482,6 +483,19 @@ void Sema::InstantiatingTemplate::Clear() {
}
}
+static std::string convertCallArgsToString(Sema &S,
+ llvm::ArrayRef<const Expr *> Args) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ llvm::ListSeparator Comma;
+ for (const Expr *Arg : Args) {
+ OS << Comma;
+ Arg->IgnoreParens()->printPretty(OS, nullptr,
+ S.Context.getPrintingPolicy());
+ }
+ return Result;
+}
+
bool Sema::InstantiatingTemplate::CheckInstantiationDepth(
SourceLocation PointOfInstantiation,
SourceRange InstantiationRange) {
@@ -770,6 +784,14 @@ void Sema::PrintInstantiationStack() {
<< cast<CXXRecordDecl>(Active->Entity) << !getLangOpts().CPlusPlus11;
break;
+ case CodeSynthesisContext::BuildingBuiltinDumpStructCall:
+ Diags.Report(Active->PointOfInstantiation,
+ diag::note_building_builtin_dump_struct_call)
+ << convertCallArgsToString(
+ *this,
+ llvm::makeArrayRef(Active->CallArgs, Active->NumCallArgs));
+ break;
+
case CodeSynthesisContext::Memoization:
break;
@@ -874,6 +896,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
case CodeSynthesisContext::DefiningSynthesizedFunction:
case CodeSynthesisContext::InitializingStructuredBinding:
case CodeSynthesisContext::MarkingClassDllexported:
+ case CodeSynthesisContext::BuildingBuiltinDumpStructCall:
// This happens in a context unrelated to template instantiation, so
// there is no SFINAE.
return None;
diff --git a/clang/test/CodeGen/builtin-dump-struct.c b/clang/test/CodeGen/builtin-dump-struct.c
new file mode 100644
index 000000000000..af0a42c5a040
--- /dev/null
+++ b/clang/test/CodeGen/builtin-dump-struct.c
@@ -0,0 +1,271 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+
+#include "Inputs/stdio.h"
+#include <stdint.h>
+
+// CHECK-DAG: @[[STR_0:.*]] = private unnamed_addr constant [3 x i8] c"%s\00",
+// CHECK-DAG: @[[STR_1:.*]] = private unnamed_addr constant [9 x i8] c"struct A\00",
+// CHECK-DAG: @[[STR_2:.*]] = private unnamed_addr constant [4 x i8] c" {\0A\00",
+// CHECK-DAG: @[[STR_4:.*]] = private unnamed_addr constant [3 x i8] c" \00",
+// CHECK-DAG: @[[STR_5:.*]] = private unnamed_addr constant [5 x i8] c"char\00",
+// CHECK-DAG: @[[STR_6:.*]] = private unnamed_addr constant [3 x i8] c"i1\00",
+// CHECK-DAG: @[[STR_7:.*]] = private unnamed_addr constant [16 x i8] c"%s%s %s = %hhd\0A\00",
+// CHECK-DAG: @[[STR_8:.*]] = private unnamed_addr constant [12 x i8] c"signed char\00",
+// CHECK-DAG: @[[STR_9:.*]] = private unnamed_addr constant [3 x i8] c"i2\00",
+// CHECK-DAG: @[[STR_10:.*]] = private unnamed_addr constant [16 x i8] c"%s%s %s = %hhu\0A\00",
+// CHECK-DAG: @[[STR_11:.*]] = private unnamed_addr constant [14 x i8] c"unsigned char\00",
+// CHECK-DAG: @[[STR_12:.*]] = private unnamed_addr constant [3 x i8] c"i3\00",
+// CHECK-DAG: @[[STR_13:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = %hd\0A\00",
+// CHECK-DAG: @[[STR_14:.*]] = private unnamed_addr constant [6 x i8] c"short\00",
+// CHECK-DAG: @[[STR_15:.*]] = private unnamed_addr constant [3 x i8] c"i4\00",
+// CHECK-DAG: @[[STR_16:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = %hu\0A\00",
+// CHECK-DAG: @[[STR_17:.*]] = private unnamed_addr constant [15 x i8] c"unsigned short\00",
+// CHECK-DAG: @[[STR_18:.*]] = private unnamed_addr constant [3 x i8] c"i5\00",
+// CHECK-DAG: @[[STR_19:.*]] = private unnamed_addr constant [14 x i8] c"%s%s %s = %d\0A\00",
+// CHECK-DAG: @[[STR_20:.*]] = private unnamed_addr constant [4 x i8] c"int\00",
+// CHECK-DAG: @[[STR_21:.*]] = private unnamed_addr constant [3 x i8] c"i6\00",
+// CHECK-DAG: @[[STR_22:.*]] = private unnamed_addr constant [14 x i8] c"%s%s %s = %u\0A\00",
+// CHECK-DAG: @[[STR_23:.*]] = private unnamed_addr constant [13 x i8] c"unsigned int\00",
+// CHECK-DAG: @[[STR_24:.*]] = private unnamed_addr constant [3 x i8] c"i7\00",
+// CHECK-DAG: @[[STR_25:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = %ld\0A\00",
+// CHECK-DAG: @[[STR_26:.*]] = private unnamed_addr constant [5 x i8] c"long\00",
+// CHECK-DAG: @[[STR_27:.*]] = private unnamed_addr constant [3 x i8] c"i8\00",
+// CHECK-DAG: @[[STR_28:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = %lu\0A\00",
+// CHECK-DAG: @[[STR_29:.*]] = private unnamed_addr constant [14 x i8] c"unsigned long\00",
+// CHECK-DAG: @[[STR_30:.*]] = private unnamed_addr constant [3 x i8] c"i9\00",
+// CHECK-DAG: @[[STR_31:.*]] = private unnamed_addr constant [16 x i8] c"%s%s %s = %lld\0A\00",
+// CHECK-DAG: @[[STR_32:.*]] = private unnamed_addr constant [10 x i8] c"long long\00",
+// CHECK-DAG: @[[STR_33:.*]] = private unnamed_addr constant [4 x i8] c"i10\00",
+// CHECK-DAG: @[[STR_34:.*]] = private unnamed_addr constant [16 x i8] c"%s%s %s = %llu\0A\00",
+// CHECK-DAG: @[[STR_35:.*]] = private unnamed_addr constant [19 x i8] c"unsigned long long\00",
+// CHECK-DAG: @[[STR_36:.*]] = private unnamed_addr constant [4 x i8] c"i11\00",
+// CHECK-DAG: @[[STR_37:.*]] = private unnamed_addr constant [14 x i8] c"%s%s %s = %f\0A\00",
+// CHECK-DAG: @[[STR_38:.*]] = private unnamed_addr constant [6 x i8] c"float\00",
+// CHECK-DAG: @[[STR_39:.*]] = private unnamed_addr constant [3 x i8] c"f1\00",
+// CHECK-DAG: @[[STR_40:.*]] = private unnamed_addr constant [7 x i8] c"double\00",
+// CHECK-DAG: @[[STR_41:.*]] = private unnamed_addr constant [3 x i8] c"f2\00",
+// CHECK-DAG: @[[STR_42:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = %Lf\0A\00",
+// CHECK-DAG: @[[STR_43:.*]] = private unnamed_addr constant [12 x i8] c"long double\00",
+// CHECK-DAG: @[[STR_44:.*]] = private unnamed_addr constant [3 x i8] c"f3\00",
+// CHECK-DAG: @[[STR_45:.*]] = private unnamed_addr constant [14 x i8] c"%s%s %s = %p\0A\00",
+// CHECK-DAG: @[[STR_46:.*]] = private unnamed_addr constant [7 x i8] c"void *\00",
+// CHECK-DAG: @[[STR_47:.*]] = private unnamed_addr constant [3 x i8] c"p1\00",
+// CHECK-DAG: @[[STR_48:.*]] = private unnamed_addr constant [19 x i8] c"%s%s %s = \22%.32s\22\0A\00",
+// CHECK-DAG: @[[STR_49:.*]] = private unnamed_addr constant [7 x i8] c"char *\00",
+// CHECK-DAG: @[[STR_50:.*]] = private unnamed_addr constant [3 x i8] c"s1\00",
+// CHECK-DAG: @[[STR_51:.*]] = private unnamed_addr constant [13 x i8] c"const char *\00",
+// CHECK-DAG: @[[STR_52:.*]] = private unnamed_addr constant [3 x i8] c"s2\00",
+// CHECK-DAG: @[[STR_53:.*]] = private unnamed_addr constant [15 x i8] c"%s%s %s = *%p\0A\00",
+// CHECK-DAG: @[[STR_54:.*]] = private unnamed_addr constant [9 x i8] c"char[10]\00",
+// CHECK-DAG: @[[STR_55:.*]] = private unnamed_addr constant [3 x i8] c"s3\00",
+// CHECK-DAG: @[[STR_56:.*]] = private unnamed_addr constant [10 x i8] c"%s%s %s =\00",
+// CHECK-DAG: @[[STR_57:.*]] = private unnamed_addr constant [9 x i8] c"struct X\00",
+// CHECK-DAG: @[[STR_58:.*]] = private unnamed_addr constant [3 x i8] c"x1\00",
+// CHECK-DAG: @[[STR_59:.*]] = private unnamed_addr constant [5 x i8] c" \00",
+// CHECK-DAG: @[[STR_60:.*]] = private unnamed_addr constant [2 x i8] c"n\00",
+// CHECK-DAG: @[[STR_61:.*]] = private unnamed_addr constant [5 x i8] c"%s}\0A\00",
+// CHECK-DAG: @[[STR_62:.*]] = private unnamed_addr constant [3 x i8] c"n1\00",
+// CHECK-DAG: @[[STR_63:.*]] = private unnamed_addr constant [3 x i8] c"n2\00",
+// CHECK-DAG: @[[STR_64:.*]] = private unnamed_addr constant [3 x i8] c"u1\00",
+// CHECK-DAG: @[[STR_65:.*]] = private unnamed_addr constant [3 x i8] c"u2\00",
+// CHECK-DAG: @[[STR_66:.*]] = private unnamed_addr constant [20 x i8] c"%s%s %s : %zu = %d\0A\00",
+// CHECK-DAG: @[[STR_67:.*]] = private unnamed_addr constant [3 x i8] c"b1\00",
+// CHECK-DAG: @[[STR_68:.*]] = private unnamed_addr constant [3 x i8] c"b2\00",
+// CHECK-DAG: @[[STR_69:.*]] = private unnamed_addr constant [13 x i8] c"_Complex int\00",
+// CHECK-DAG: @[[STR_70:.*]] = private unnamed_addr constant [4 x i8] c"ci1\00",
+// CHECK-DAG: @[[STR_71:.*]] = private unnamed_addr constant [16 x i8] c"_Complex double\00",
+// CHECK-DAG: @[[STR_72:.*]] = private unnamed_addr constant [4 x i8] c"cd1\00",
+// CHECK-DAG: @[[STR_73:.*]] = private unnamed_addr constant [3 x i8] c"}\0A\00",
+
+struct X {
+ int n;
+};
+
+struct A {
+ char i1;
+ signed char i2;
+ unsigned char i3;
+ short i4;
+ unsigned short i5;
+ int i6;
+ unsigned int i7;
+ long i8;
+ unsigned long i9;
+ long long i10;
+ unsigned long long i11;
+
+ float f1;
+ double f2;
+ long double f3;
+
+ void *p1;
+ char *s1;
+ const char *s2;
+ char s3[10];
+
+ struct X x1;
+
+ struct {
+ int n1;
+ struct X n2;
+ };
+ union {
+ int u1;
+ int u2;
+ };
+
+ int b1 : 5;
+ int : 0;
+ int b2 : 3;
+ int : 5;
+
+ _Complex int ci1;
+ _Complex double cd1;
+};
+
+int printf(const char *fmt, ...);
+
+// CHECK-LABEL: define {{.*}} @test(
+void test(struct A *a) {
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_0]], ptr noundef @[[STR_1]])
+
+ // CHECK: %[[VAL_0:.*]] = load ptr, ptr %[[VAL_a_addr:.*]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_2]])
+
+ // CHECK: %[[VAL_i1:.*]] = getelementptr inbounds %[[VAL_struct_A:.*]], ptr %[[VAL_0]], i32 0, i32 0
+ // CHECK: %[[VAL_1:.*]] = load i8, ptr %[[VAL_i1]],
+ // CHECK: %[[VAL_conv:.*]] = sext i8 %[[VAL_1]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_7]], ptr noundef @[[STR_4]], ptr noundef @[[STR_5]], ptr noundef @[[STR_6]], i32 noundef %[[VAL_conv]])
+
+ // CHECK: %[[VAL_i2:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 1
+ // CHECK: %[[VAL_2:.*]] = load i8, ptr %[[VAL_i2]],
+ // CHECK: %[[VAL_conv3:.*]] = sext i8 %[[VAL_2]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_7]], ptr noundef @[[STR_4]], ptr noundef @[[STR_8]], ptr noundef @[[STR_9]], i32 noundef %[[VAL_conv3]])
+
+ // CHECK: %[[VAL_i3:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 2
+ // CHECK: %[[VAL_3:.*]] = load i8, ptr %[[VAL_i3]],
+ // CHECK: %[[VAL_conv5:.*]] = zext i8 %[[VAL_3]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_10]], ptr noundef @[[STR_4]], ptr noundef @[[STR_11]], ptr noundef @[[STR_12]], i32 noundef %[[VAL_conv5]])
+
+ // CHECK: %[[VAL_i4:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 3
+ // CHECK: %[[VAL_4:.*]] = load i16, ptr %[[VAL_i4]],
+ // CHECK: %[[VAL_conv7:.*]] = sext i16 %[[VAL_4]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_13]], ptr noundef @[[STR_4]], ptr noundef @[[STR_14]], ptr noundef @[[STR_15]], i32 noundef %[[VAL_conv7]])
+
+ // CHECK: %[[VAL_i5:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 4
+ // CHECK: %[[VAL_5:.*]] = load i16, ptr %[[VAL_i5]],
+ // CHECK: %[[VAL_conv9:.*]] = zext i16 %[[VAL_5]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_16]], ptr noundef @[[STR_4]], ptr noundef @[[STR_17]], ptr noundef @[[STR_18]], i32 noundef %[[VAL_conv9]])
+
+ // CHECK: %[[VAL_i6:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 5
+ // CHECK: %[[VAL_6:.*]] = load i32, ptr %[[VAL_i6]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_21]], i32 noundef %[[VAL_6]])
+
+ // CHECK: %[[VAL_i7:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 6
+ // CHECK: %[[VAL_7:.*]] = load i32, ptr %[[VAL_i7]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_22]], ptr noundef @[[STR_4]], ptr noundef @[[STR_23]], ptr noundef @[[STR_24]], i32 noundef %[[VAL_7]])
+
+ // CHECK: %[[VAL_i8:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 7
+ // CHECK: %[[VAL_8:.*]] = load i64, ptr %[[VAL_i8]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_25]], ptr noundef @[[STR_4]], ptr noundef @[[STR_26]], ptr noundef @[[STR_27]], i64 noundef %[[VAL_8]])
+
+ // CHECK: %[[VAL_i9:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 8
+ // CHECK: %[[VAL_9:.*]] = load i64, ptr %[[VAL_i9]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_28]], ptr noundef @[[STR_4]], ptr noundef @[[STR_29]], ptr noundef @[[STR_30]], i64 noundef %[[VAL_9]])
+
+ // CHECK: %[[VAL_i10:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 9
+ // CHECK: %[[VAL_10:.*]] = load i64, ptr %[[VAL_i10]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_31]], ptr noundef @[[STR_4]], ptr noundef @[[STR_32]], ptr noundef @[[STR_33]], i64 noundef %[[VAL_10]])
+
+ // CHECK: %[[VAL_i11:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 10
+ // CHECK: %[[VAL_11:.*]] = load i64, ptr %[[VAL_i11]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_34]], ptr noundef @[[STR_4]], ptr noundef @[[STR_35]], ptr noundef @[[STR_36]], i64 noundef %[[VAL_11]])
+
+ // CHECK: %[[VAL_f1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 11
+ // CHECK: %[[VAL_12:.*]] = load float, ptr %[[VAL_f1]],
+ // CHECK: %[[VAL_conv17:.*]] = fpext float %[[VAL_12]] to double
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_37]], ptr noundef @[[STR_4]], ptr noundef @[[STR_38]], ptr noundef @[[STR_39]], double noundef %[[VAL_conv17]])
+
+ // CHECK: %[[VAL_f2:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 12
+ // CHECK: %[[VAL_13:.*]] = load double, ptr %[[VAL_f2]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_37]], ptr noundef @[[STR_4]], ptr noundef @[[STR_40]], ptr noundef @[[STR_41]], double noundef %[[VAL_13]])
+
+ // CHECK: %[[VAL_f3:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 13
+ // CHECK: %[[VAL_14:.*]] = load x86_fp80, ptr %[[VAL_f3]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_42]], ptr noundef @[[STR_4]], ptr noundef @[[STR_43]], ptr noundef @[[STR_44]], x86_fp80 noundef %[[VAL_14]])
+
+ // CHECK: %[[VAL_p1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 14
+ // CHECK: %[[VAL_15:.*]] = load ptr, ptr %[[VAL_p1]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_45]], ptr noundef @[[STR_4]], ptr noundef @[[STR_46]], ptr noundef @[[STR_47]], ptr noundef %[[VAL_15]])
+
+ // CHECK: %[[VAL_s1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 15
+ // CHECK: %[[VAL_16:.*]] = load ptr, ptr %[[VAL_s1]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_48]], ptr noundef @[[STR_4]], ptr noundef @[[STR_49]], ptr noundef @[[STR_50]], ptr noundef %[[VAL_16]])
+
+ // CHECK: %[[VAL_s2:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 16
+ // CHECK: %[[VAL_17:.*]] = load ptr, ptr %[[VAL_s2]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_48]], ptr noundef @[[STR_4]], ptr noundef @[[STR_51]], ptr noundef @[[STR_52]], ptr noundef %[[VAL_17]])
+
+ // CHECK: %[[VAL_s3:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 17
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_53]], ptr noundef @[[STR_4]], ptr noundef @[[STR_54]], ptr noundef @[[STR_55]], ptr noundef %[[VAL_s3]])
+
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_56]], ptr noundef @[[STR_4]], ptr noundef @[[STR_57]], ptr noundef @[[STR_58]])
+
+ // CHECK: %[[VAL_x1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 18
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_2]])
+
+ // CHECK: %[[VAL_n:.*]] = getelementptr inbounds %[[VAL_struct_X:.*]], ptr %[[VAL_x1]], i32 0, i32 0
+ // CHECK: %[[VAL_18:.*]] = load i32, ptr %[[VAL_n]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_59]], ptr noundef @[[STR_20]], ptr noundef @[[STR_60]], i32 noundef %[[VAL_18]])
+
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_61]], ptr noundef @[[STR_4]])
+
+ // CHECK: %[[VAL_19:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 19
+ // CHECK: %[[VAL_n1:.*]] = getelementptr inbounds %[[VAL_struct_anon:.*]], ptr %[[VAL_19]], i32 0, i32 0
+ // CHECK: %[[VAL_20:.*]] = load i32, ptr %[[VAL_n1]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_62]], i32 noundef %[[VAL_20]])
+
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_56]], ptr noundef @[[STR_4]], ptr noundef @[[STR_57]], ptr noundef @[[STR_63]])
+
+ // CHECK: %[[VAL_21:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 19
+ // CHECK: %[[VAL_n2:.*]] = getelementptr inbounds %[[VAL_struct_anon]], ptr %[[VAL_21]], i32 0, i32 1
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_2]])
+
+ // CHECK: %[[VAL_n32:.*]] = getelementptr inbounds %[[VAL_struct_X]], ptr %[[VAL_n2]], i32 0, i32 0
+ // CHECK: %[[VAL_22:.*]] = load i32, ptr %[[VAL_n32]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_59]], ptr noundef @[[STR_20]], ptr noundef @[[STR_60]], i32 noundef %[[VAL_22]])
+
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_61]], ptr noundef @[[STR_4]])
+
+ // CHECK: %[[VAL_23:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 20
+ // CHECK: %[[VAL_24:.*]] = load i32, ptr %[[VAL_23]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_64]], i32 noundef %[[VAL_24]])
+
+ // CHECK: %[[VAL_25:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 20
+ // CHECK: %[[VAL_26:.*]] = load i32, ptr %[[VAL_25]],
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_19]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_65]], i32 noundef %[[VAL_26]])
+
+ // CHECK: %[[VAL_b1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 21
+ // CHECK: %[[VAL_bf_load:.*]] = load i8, ptr %[[VAL_b1]],
+ // CHECK: %[[VAL_bf_shl:.*]] = shl i8 %[[VAL_bf_load]], 3
+ // CHECK: %[[VAL_bf_ashr:.*]] = ashr i8 %[[VAL_bf_shl]], 3
+ // CHECK: %[[VAL_bf_cast:.*]] = sext i8 %[[VAL_bf_ashr]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_66]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_67]], i64 noundef 5, i32 noundef %[[VAL_bf_cast]])
+
+ // CHECK: %[[VAL_b2:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 23
+ // CHECK: %[[VAL_bf_load38:.*]] = load i8, ptr %[[VAL_b2]],
+ // CHECK: %[[VAL_bf_shl39:.*]] = shl i8 %[[VAL_bf_load38]], 5
+ // CHECK: %[[VAL_bf_ashr40:.*]] = ashr i8 %[[VAL_bf_shl39]], 5
+ // CHECK: %[[VAL_bf_cast41:.*]] = sext i8 %[[VAL_bf_ashr40]] to i32
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_66]], ptr noundef @[[STR_4]], ptr noundef @[[STR_20]], ptr noundef @[[STR_68]], i64 noundef 3, i32 noundef %[[VAL_bf_cast41]])
+
+ // CHECK: %[[VAL_ci1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 24
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_53]], ptr noundef @[[STR_4]], ptr noundef @[[STR_69]], ptr noundef @[[STR_70]], ptr noundef %[[VAL_ci1]])
+
+ // CHECK: %[[VAL_cd1:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_0]], i32 0, i32 25
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_53]], ptr noundef @[[STR_4]], ptr noundef @[[STR_71]], ptr noundef @[[STR_72]], ptr noundef %[[VAL_cd1]])
+
+ // CHECK: call {{.*}} @printf(ptr noundef @[[STR_73]])
+ __builtin_dump_struct(a, printf);
+
+}
diff --git a/clang/test/CodeGen/dump-struct-builtin.c b/clang/test/CodeGen/dump-struct-builtin.c
deleted file mode 100644
index 730119c897dd..000000000000
--- a/clang/test/CodeGen/dump-struct-builtin.c
+++ /dev/null
@@ -1,809 +0,0 @@
-// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
-
-#include "Inputs/stdio.h"
-#include <stdint.h>
-
-// CHECK: @__const.unit1.a = private unnamed_addr constant %struct.U1A { i16 12 }, align 2
-// CHECK-NEXT: [[STRUCT_STR_U1:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U1A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U1:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U1:@[0-9]+]] = private unnamed_addr constant [15 x i8] c" short a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U1:@[0-9]+]] = private unnamed_addr constant [5 x i8] c"%hd\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U1:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit2.a = private unnamed_addr constant %struct.U2A { i16 12 }, align 2
-// CHECK-NEXT: [[STRUCT_STR_U2:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U2A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U2:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U2:@[0-9]+]] = private unnamed_addr constant [24 x i8] c" unsigned short a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U2:@[0-9]+]] = private unnamed_addr constant [5 x i8] c"%hu\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U2:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit3.a = private unnamed_addr constant %struct.U3A { i32 12 }, align 4
-// CHECK-NEXT: [[STRUCT_STR_U3:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U3A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U3:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U3:@[0-9]+]] = private unnamed_addr constant [13 x i8] c" int a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U3:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U3:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit4.a = private unnamed_addr constant %struct.U4A { i32 12 }, align 4
-// CHECK-NEXT: [[STRUCT_STR_U4:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U4A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U4:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U4:@[0-9]+]] = private unnamed_addr constant [22 x i8] c" unsigned int a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U4:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%u\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U4:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit5.a = private unnamed_addr constant %struct.U5A { i64 12 }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U5:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U5A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U5:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U5:@[0-9]+]] = private unnamed_addr constant [14 x i8] c" long a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U5:@[0-9]+]] = private unnamed_addr constant [5 x i8] c"%ld\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U5:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit6.a = private unnamed_addr constant %struct.U6A { i64 12 }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U6:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U6A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U6:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U6:@[0-9]+]] = private unnamed_addr constant [23 x i8] c" unsigned long a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U6:@[0-9]+]] = private unnamed_addr constant [5 x i8] c"%lu\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U6:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit7.a = private unnamed_addr constant %struct.U7A { i64 12 }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U7:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U7A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U7:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U7:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" long long a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U7:@[0-9]+]] = private unnamed_addr constant [6 x i8] c"%lld\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U7:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit8.a = private unnamed_addr constant %struct.U8A { i64 12 }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U8:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U8A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U8:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U8:@[0-9]+]] = private unnamed_addr constant [28 x i8] c" unsigned long long a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U8:@[0-9]+]] = private unnamed_addr constant [6 x i8] c"%llu\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U8:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit9.a = private unnamed_addr constant %struct.U9A { i8 97 }, align 1
-// CHECK-NEXT: [[STRUCT_STR_U9:@[0-9]+]] = private unnamed_addr constant [12 x i8] c"struct U9A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U9:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U9:@[0-9]+]] = private unnamed_addr constant [14 x i8] c" char a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U9:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U9:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @.str = private unnamed_addr constant [4 x i8] c"LSE\00", align 1
-// CHECK: @__const.unit10.a = private unnamed_addr constant %struct.U10A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0) }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U10:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U10A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U10:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U10:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" char * a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U10:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U10:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit11.a = private unnamed_addr constant %struct.U11A { i8* inttoptr (i64 305419896 to i8*) }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U11:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U11A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U11:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U11:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" void * a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U11:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%p\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U11:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit12.a = private unnamed_addr constant %struct.U12A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0) }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U12:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U12A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U12:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U12:@[0-9]+]] = private unnamed_addr constant [22 x i8] c" const char * a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U12:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U12:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit13.a = private unnamed_addr constant %struct.U13A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0) }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U13:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U13A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U13:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U13:@[0-9]+]] = private unnamed_addr constant [24 x i8] c" const charstar a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U13:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U13:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit14.a = private unnamed_addr constant %struct.U14A { double 0x3FF1F9ACFFA7EB6C }, align 8
-// CHECK-NEXT: [[STRUCT_STR_U14:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U14A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U14:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U14:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" double a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U14:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%f\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U14:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit15.a = private unnamed_addr constant %struct.U15A { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
-// CHECK-NEXT: [[STRUCT_STR_U15:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U15A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" int[3] a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U15:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%p\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit16.a = private unnamed_addr constant %struct.U16A { i8 12 }, align 1
-// CHECK-NEXT: [[STRUCT_STR_U16:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U16A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U16:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U16:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" uint8_t a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U16:@[0-9]+]] = private unnamed_addr constant [6 x i8] c"%hhu\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U16:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit17.a = private unnamed_addr constant %struct.U17A { i8 12 }, align 1
-// CHECK-NEXT: [[STRUCT_STR_U17:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U17A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U17:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U17:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" int8_t a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U17:@[0-9]+]] = private unnamed_addr constant [6 x i8] c"%hhd\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U17:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit18.a = private unnamed_addr constant %struct.U18A { x86_fp80 0xK3FFF8FCD67FD3F5B6000 }, align 16
-// CHECK-NEXT: [[STRUCT_STR_U18:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U18A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U18:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U18:@[0-9]+]] = private unnamed_addr constant [21 x i8] c" long double a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U18:@[0-9]+]] = private unnamed_addr constant [5 x i8] c"%Lf\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U18:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit19.a = private unnamed_addr constant %struct.U19B { %struct.U19A { i32 2022 } }, align 4
-// CHECK-NEXT: [[STRUCT_STR_U19:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U19B \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U19:@[0-9]+]] = private unnamed_addr constant [21 x i8] c" struct U19A a = \00", align 1
-// CHECK-NEXT: [[NESTED_STRUCT_L_BRACE_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[NESTED_FIELD_U19:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" int a = \00", align 1
-// CHECK-NEXT: [[NESTED_FORMAT_U19:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
-// CHECK-NEXT: [[NESTED_STRUCT_R_BRACE_U19:@[0-9]+]] = private unnamed_addr constant [7 x i8] c" }\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-// CHECK: @__const.unit20.a = private unnamed_addr constant %struct.U20A { i8 1 }, align 1
-// CHECK-NEXT: [[STRUCT_STR_U20:@[0-9]+]] = private unnamed_addr constant [13 x i8] c"struct U20A \00", align 1
-// CHECK-NEXT: [[STRUCT_L_BRACE_U20:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"{\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U20:@[0-9]+]] = private unnamed_addr constant [15 x i8] c" _Bool a = \00", align 1
-// CHECK-NEXT: [[FORMAT_U20:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
-// CHECK-NEXT: [[STRUCT_R_BRACE_U20:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
-
-int printf(const char *fmt, ...) {
- return 0;
-}
-
-void unit1(void) {
- struct U1A {
- short a;
- };
-
- struct U1A a = {
- .a = 12,
- };
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U1]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U1]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[FIELD_U1]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[FORMAT_U1]], i32 0, i32 0), i16 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U1]], i32 0, i32 0))
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit2(void) {
- struct U2A {
- unsigned short a;
- };
-
- struct U2A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U2]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U2]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[FIELD_U2]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[FORMAT_U2]], i32 0, i32 0), i16 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U2]], i32 0, i32 0))
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit3(void) {
- struct U3A {
- int a;
- };
-
- struct U3A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U3]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U3]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[FIELD_U3]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U3]], i32 0, i32 0), i32 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U3]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit4(void) {
- struct U4A {
- unsigned int a;
- };
-
- struct U4A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U4]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U4]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[FIELD_U4]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U4]], i32 0, i32 0), i32 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U4]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit5(void) {
- struct U5A {
- long a;
- };
-
- struct U5A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U5]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U5]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[FIELD_U5]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[FORMAT_U5]], i32 0, i32 0), i64 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U5]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit6(void) {
- struct U6A {
- unsigned long a;
- };
-
- struct U6A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U6]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U6]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([23 x i8], [23 x i8]* [[FIELD_U6]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[FORMAT_U6]], i32 0, i32 0), i64 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U6]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit7(void) {
- struct U7A {
- long long a;
- };
-
- struct U7A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U7]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U7]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U7]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[FORMAT_U7]], i32 0, i32 0), i64 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U7]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit8(void) {
- struct U8A {
- unsigned long long a;
- };
-
- struct U8A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U8]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U8]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* [[FIELD_U8]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[FORMAT_U8]], i32 0, i32 0), i64 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U8]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit9(void) {
- struct U9A {
- char a;
- };
-
- struct U9A a = {
- .a = 'a',
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[STRUCT_STR_U9]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U9]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[FIELD_U9]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U9]], i32 0, i32 0), i8 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U9]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit10(void) {
- struct U10A {
- char *a;
- };
-
- struct U10A a = {
- .a = "LSE",
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U10]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U10]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U10]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U10]], i32 0, i32 0), i8* [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U10]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit11(void) {
- struct U11A {
- void *a;
- };
-
- struct U11A a = {
- .a = (void *)0x12345678,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U11]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U11]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U11]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U11]], i32 0, i32 0), i8* [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U11]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit12(void) {
- struct U12A {
- const char *a;
- };
-
- struct U12A a = {
- .a = "LSE",
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U12]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U12]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[FIELD_U12]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U12A, %struct.U12A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U12]], i32 0, i32 0), i8* [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U12]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit13(void) {
- typedef char *charstar;
- struct U13A {
- const charstar a;
- };
-
- struct U13A a = {
- .a = "LSE",
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U13]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U13]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[FIELD_U13]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U13A, %struct.U13A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U13]], i32 0, i32 0), i8* [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U13]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit14(void) {
- struct U14A {
- double a;
- };
-
- struct U14A a = {
- .a = 1.123456,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U14]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U14]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U14]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U14A, %struct.U14A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load double, double* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U14]], i32 0, i32 0), double [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U14]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit15(void) {
- struct U15A {
- int a[3];
- };
-
- struct U15A a = {
- .a = {1, 2, 3},
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U15]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U15]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U15]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load [3 x i32], [3 x i32]* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U15]], i32 0, i32 0), [3 x i32] [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U15]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit16(void) {
- struct U16A {
- uint8_t a;
- };
-
- struct U16A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U16]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U16]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[FIELD_U16]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U16A, %struct.U16A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[FORMAT_U16]], i32 0, i32 0), i8 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U16]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit17(void) {
- struct U17A {
- int8_t a;
- };
-
- struct U17A a = {
- .a = 12,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U17]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U17]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U17]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U17A, %struct.U17A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[FORMAT_U17]], i32 0, i32 0), i8 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U17]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit18(void) {
- struct U18A {
- long double a;
- };
-
- struct U18A a = {
- .a = 1.123456,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U18]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U18]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[FIELD_U18]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U18A, %struct.U18A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load x86_fp80, x86_fp80* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[FORMAT_U18]], i32 0, i32 0), x86_fp80 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U18]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit19(void) {
-
- struct U19A {
- int a;
- };
-
- struct U19B {
- struct U19A a;
- };
-
- struct U19B a = {
- .a.a = 2022
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U19]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U19]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[FIELD_U19]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U19B, %struct.U19B* %a, i32 0, i32 0
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[NESTED_STRUCT_L_BRACE_U19]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[NESTED_FIELD_U19]], i32 0, i32 0))
- // CHECK: [[RES2:%.*]] = getelementptr inbounds %struct.U19A, %struct.U19A* [[RES1]], i32 0, i32 0
- // CHECK: [[LOAD2:%.*]] = load i32, i32* [[RES2]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[NESTED_FORMAT_U19]], i32 0, i32 0), i32 [[LOAD2]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[NESTED_STRUCT_R_BRACE_U19]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U19]], i32 0, i32 0))
- __builtin_dump_struct(&a, &printf);
-}
-
-void unit20(void) {
- struct U20A {
- _Bool a;
- };
-
- struct U20A a = {
- .a = 1,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[STRUCT_STR_U20]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_L_BRACE_U20]], i32 0, i32 0))
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[FIELD_U20]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U20A, %struct.U20A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]],
- // CHECK: [[TOBOOL:%.*]] = trunc i8 [[LOAD1]] to i1
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[FORMAT_U20]], i32 0, i32 0), i1 [[TOBOOL]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[STRUCT_R_BRACE_U20]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
-}
-
-void test1(void) {
- struct T1A {
- int a;
- char *b;
- };
-
- struct T1A a = {
- .a = 12,
- .b = "LSE",
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES2:%.*]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 1
- // CHECK: [[LOAD2:%.*]] = load i8*, i8** [[RES2]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD2]])
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test2(void) {
- struct T2A {
- int a;
- };
-
- struct T2B {
- int b;
- struct T2A a;
- };
-
- struct T2B b = {
- .b = 24,
- .a = {
- .a = 12,
- }
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[NESTED_STRUCT:%.*]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 1
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES2:%.*]] = getelementptr inbounds %struct.T2A, %struct.T2A* [[NESTED_STRUCT]], i32 0, i32 0
- // CHECK: [[LOAD2:%.*]] = load i32, i32* [[RES2]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD2]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&b, &printf);
-}
-
-void test3(void) {
- struct T3A {
- union {
- int a;
- char b[4];
- };
- };
-
- struct T3A a = {
- .a = 42,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T3A, %struct.T3A* %a, i32 0, i32 0
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %union.anon* [[RES1]] to i32*
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[BC1]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC2:%.*]] = bitcast %union.anon* [[RES1]] to [4 x i8]*
- // CHECK: [[LOAD2:%.*]] = load [4 x i8], [4 x i8]* [[BC2]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test4(void) {
- struct T4A {
- union {
- struct {
- void *a;
- };
- struct {
- unsigned long b;
- };
- };
- };
-
- struct T4A a = {
- .a = (void *)0x12345678,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T4A, %struct.T4A* %a, i32 0, i32 0
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %union.anon.0* [[RES1]] to %struct.anon*
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES2:%.*]] = getelementptr inbounds %struct.anon, %struct.anon* [[BC1]], i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES2]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC2:%.*]] = bitcast %union.anon.0* [[RES1]] to %struct.anon.1*
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES3:%.*]] = getelementptr inbounds %struct.anon.1, %struct.anon.1* [[BC2]], i32 0, i32 0
- // CHECK: [[LOAD2:%.*]] = load i64, i64* [[RES3]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD2]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test5(void) {
- struct T5A {
- unsigned a : 1;
- };
-
- struct T5A a = {
- .a = 0,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %struct.T5A* %a to i8*
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]],
- // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1
- // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST1]])
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test6(void) {
- struct T6A {
- unsigned a : 1;
- unsigned b : 1;
- unsigned c : 1;
- };
-
- struct T6A a = {
- .a = 1,
- .b = 0,
- .c = 1,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %struct.T6A* %a to i8*
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]],
- // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1
- // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC2:%.*]] = bitcast %struct.T6A* %a to i8*
- // CHECK: [[LOAD2:%.*]] = load i8, i8* [[BC2]], align 4
- // CHECK: [[LSHR2:%.*]] = lshr i8 [[LOAD2]], 1
- // CHECK: [[CLEAR2:%.*]] = and i8 [[LSHR2]], 1
- // CHECK: [[CAST2:%.*]] = zext i8 [[CLEAR2]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST2]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC3:%.*]] = bitcast %struct.T6A* %a to i8*
- // CHECK: [[LOAD3:%.*]] = load i8, i8* [[BC3]], align 4
- // CHECK: [[LSHR3:%.*]] = lshr i8 [[LOAD3]], 2
- // CHECK: [[CLEAR3:%.*]] = and i8 [[LSHR3]], 1
- // CHECK: [[CAST3:%.*]] = zext i8 [[CLEAR3]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST3]])
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test7(void) {
-
- struct T7A {
- unsigned a : 1;
- };
-
- struct T7B {
- struct T7A a;
- unsigned b : 1;
- };
-
- struct T7B a = {
- .a = {.a = 0},
- .b = 1,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T7B, %struct.T7B* %a, i32 0, i32 0
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %struct.T7A* [[RES1]] to i8*
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]],
- // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1
- // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES2:%.*]] = getelementptr inbounds %struct.T7B, %struct.T7B* %a, i32 0, i32 1
- // CHECK: [[LOAD2:%.*]] = load i8, i8* [[RES2]], align 4
- // CHECK: [[CLEAR2:%.*]] = and i8 [[LOAD2]], 1
- // CHECK: [[CAST2:%.*]] = zext i8 [[CLEAR2]] to i32
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[CAST2]])
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
-void test8(void) {
- struct T8A {
- unsigned c : 1;
- unsigned : 3;
- unsigned : 0;
- unsigned b;
- };
-
- struct T8A a = {
- .b = 2022,
- };
-
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %struct.T8A* %a to i8*
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]],
- // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1
- // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[CAST1]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC2:%.*]] = bitcast %struct.T8A* %a to i8*
- // CHECK: [[LOAD2:%.*]] = load i8, i8* [[BC2]],
- // CHECK: [[LSHR2:%.*]] = lshr i8 [[LOAD2]], 1
- // CHECK: [[CLEAR2:%.*]] = and i8 [[LSHR2]], 7
- // CHECK: [[CAST2:%.*]] = zext i8 [[CLEAR2]] to i32
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 0, i32 0), i32 [[CAST2]])
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES3:%.*]] = getelementptr inbounds %struct.T8A, %struct.T8A* %a, i32 0, i32 1
- // CHECK: [[LOAD3:%.*]] = load i32, i32* [[RES3]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({{.*}}, i32 [[LOAD3]])
- // CHECK: call i32 (i8*, ...) @printf(
- __builtin_dump_struct(&a, &printf);
-}
-
diff --git a/clang/test/CodeGenCXX/builtin-dump-struct.cpp b/clang/test/CodeGenCXX/builtin-dump-struct.cpp
new file mode 100644
index 000000000000..4f5f5b924c6b
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-dump-struct.cpp
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu %s -emit-llvm -o - | FileCheck %s
+
+// CHECK-DAG: @[[STR_0:.*]] = {{.*}} [3 x i8] c"%s\00",
+// CHECK-DAG: @[[STR_1:.*]] = {{.*}} [2 x i8] c"C\00",
+// CHECK-DAG: @[[STR_2:.*]] = {{.*}} [4 x i8] c" {\0A\00",
+// CHECK-DAG: @[[STR_3:.*]] = {{.*}} [5 x i8] c"%s%s\00",
+// CHECK-DAG: @[[STR_4:.*]] = {{.*}} [3 x i8] c" \00",
+// CHECK-DAG: @[[STR_5:.*]] = {{.*}} [2 x i8] c"A\00",
+// CHECK-DAG: @[[STR_6:.*]] = {{.*}} [14 x i8] c"%s%s %s = %d\0A\00",
+// CHECK-DAG: @[[STR_7:.*]] = {{.*}} [5 x i8] c" \00",
+// CHECK-DAG: @[[STR_8:.*]] = {{.*}} [4 x i8] c"int\00",
+// CHECK-DAG: @[[STR_9:.*]] = {{.*}} [2 x i8] c"n\00",
+// CHECK-DAG: @[[STR_10:.*]] = {{.*}} [5 x i8] c"%s}\0A\00",
+// CHECK-DAG: @[[STR_11:.*]] = {{.*}} [2 x i8] c"B\00",
+// CHECK-DAG: @[[STR_12:.*]] = {{.*}} [10 x i8] c"%s%s %s =\00",
+// CHECK-DAG: @[[STR_13:.*]] = {{.*}} [2 x i8] c"a\00",
+// CHECK-DAG: @[[STR_14:.*]] = {{.*}} [15 x i8] c"%s%s %s = *%p\0A\00",
+// CHECK-DAG: @[[STR_15:.*]] = {{.*}} [2 x i8] c"X\00",
+// CHECK-DAG: @[[STR_16:.*]] = {{.*}} [2 x i8] c"x\00",
+// CHECK-DAG: @[[STR_17:.*]] = {{.*}} [2 x i8] c"f\00",
+// CHECK-DAG: @[[STR_18:.*]] = {{.*}} [2 x i8] c"g\00",
+// CHECK-DAG: @[[STR_19:.*]] = {{.*}} [3 x i8] c"}\0A\00",
+
+struct A { int n; };
+struct B { int n; };
+class X { private: int n; };
+struct C : A, B { A a; X x; int f, g; };
+
+template<typename ...T> int format(int a, const char *str, T ...);
+
+int f();
+
+// CHECK-LABEL: define {{.*}} @_Z1gR1C(
+void g(C &c) {
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_0]], ptr noundef @[[STR_1]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 {{.*}}, ptr noundef @[[STR_2]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_EEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_3]], ptr noundef @[[STR_4]], ptr noundef @[[STR_5]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 {{.*}}, ptr noundef @[[STR_2]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_n:.*]] = getelementptr inbounds %[[VAL_struct_A:.*]], ptr %[[VAL_0:.*]], i32 0, i32 0
+ // CHECK: %[[VAL_1:.*]] = load i32, ptr %[[VAL_n]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_6]], ptr noundef @[[STR_7]], ptr noundef @[[STR_8]], ptr noundef @[[STR_9]], i32 noundef %[[VAL_1]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_10]], ptr noundef @[[STR_4]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_EEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_3]], ptr noundef @[[STR_4]], ptr noundef @[[STR_11]])
+
+ // CHECK: %[[VAL_2:.*]] = icmp eq ptr %[[VAL_0]], null
+ // CHECK: br i1 %[[VAL_2]],
+
+ // CHECK: %[[VAL_add_ptr:.*]] = getelementptr inbounds i8, ptr %[[VAL_0]], i64 4
+ // CHECK: br label
+
+ // CHECK: %[[VAL_cast_result:.*]] = phi
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 {{.*}}, ptr noundef @[[STR_2]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_n17:.*]] = getelementptr inbounds %[[VAL_struct_B:.*]], ptr %[[VAL_cast_result]], i32 0, i32 0
+ // CHECK: %[[VAL_3:.*]] = load i32, ptr %[[VAL_n17]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_6]], ptr noundef @[[STR_7]], ptr noundef @[[STR_8]], ptr noundef @[[STR_9]], i32 noundef %[[VAL_3]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_10]], ptr noundef @[[STR_4]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_EEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_12]], ptr noundef @[[STR_4]], ptr noundef @[[STR_5]], ptr noundef @[[STR_13]])
+
+ // CHECK: %[[VAL_a:.*]] = getelementptr inbounds %[[VAL_struct_C:.*]], ptr %[[VAL_0]], i32 0, i32 2
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 {{.*}}, ptr noundef @[[STR_2]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_n26:.*]] = getelementptr inbounds %[[VAL_struct_A]], ptr %[[VAL_a]], i32 0, i32 0
+ // CHECK: %[[VAL_4:.*]] = load i32, ptr %[[VAL_n26]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_6]], ptr noundef @[[STR_7]], ptr noundef @[[STR_8]], ptr noundef @[[STR_9]], i32 noundef %[[VAL_4]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJPKcEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_10]], ptr noundef @[[STR_4]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_x:.*]] = getelementptr inbounds %[[VAL_struct_C]], ptr %[[VAL_0]], i32 0, i32 3
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_P1XEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_14]], ptr noundef @[[STR_4]], ptr noundef @[[STR_15]], ptr noundef @[[STR_16]], ptr noundef %[[VAL_x]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_f:.*]] = getelementptr inbounds %[[VAL_struct_C]], ptr %[[VAL_0]], i32 0, i32 4
+ // CHECK: %[[VAL_5:.*]] = load i32, ptr %[[VAL_f]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_6]], ptr noundef @[[STR_4]], ptr noundef @[[STR_8]], ptr noundef @[[STR_17]], i32 noundef %[[VAL_5]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: %[[VAL_g:.*]] = getelementptr inbounds %[[VAL_struct_C]], ptr %[[VAL_0]], i32 0, i32 5
+ // CHECK: %[[VAL_6:.*]] = load i32, ptr %[[VAL_g]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 {{.*}}, ptr noundef @[[STR_6]], ptr noundef @[[STR_4]], ptr noundef @[[STR_8]], ptr noundef @[[STR_18]], i32 noundef %[[VAL_6]])
+
+ // CHECK: call {{.*}} @_Z1fv()
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 {{.*}}, ptr noundef @[[STR_19]])
+ __builtin_dump_struct(&c, format, f());
+}
+
+// CHECK-LABEL: define {{.*}} @_Z1hR1X(
+void h(X &x) {
+ // CHECK: %[[VAL_x_addr:.*]] = alloca ptr,
+ // CHECK: store ptr %[[VAL_x]], ptr %[[VAL_x_addr]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcEEiiS1_DpT_(i32 noundef 0, ptr noundef @[[STR_0]], ptr noundef @[[STR_15]])
+
+ // CHECK: %[[VAL_0:.*]] = load ptr, ptr %[[VAL_x_addr]],
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 noundef 0, ptr noundef @[[STR_2]])
+
+ // CHECK: %[[VAL_n:.*]] = getelementptr inbounds %[[VAL_class_X:.*]], ptr %[[VAL_0]], i32 0, i32 0
+ // CHECK: %[[VAL_1:.*]] = load i32, ptr %[[VAL_n]],
+ // CHECK: call {{.*}} @_Z6formatIJPKcS1_S1_iEEiiS1_DpT_(i32 noundef 0, ptr noundef @[[STR_6]], ptr noundef @[[STR_4]], ptr noundef @[[STR_8]], ptr noundef @[[STR_9]], i32 noundef %[[VAL_1]])
+
+ // CHECK: call {{.*}} @_Z6formatIJEEiiPKcDpT_(i32 noundef 0, ptr noundef @[[STR_19]])
+ __builtin_dump_struct(&x, format, 0);
+}
diff --git a/clang/test/Sema/builtin-dump-struct.c b/clang/test/Sema/builtin-dump-struct.c
index d898bac4103b..d0415a22732f 100644
--- a/clang/test/Sema/builtin-dump-struct.c
+++ b/clang/test/Sema/builtin-dump-struct.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only -fno-spell-checking -Wno-strict-prototypes -verify %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only -fno-spell-checking -Wno-strict-prototypes -verify %s -fblocks
void invalid_uses(void) {
struct A {
@@ -8,23 +8,22 @@ void invalid_uses(void) {
int (*goodfunc)(const char *, ...);
int (*badfunc1)(const char *);
int (*badfunc2)(int, ...);
- void (*badfunc3)(const char *, ...);
- int (*badfunc4)(char *, ...);
- int (*badfunc5)(void);
+ int (*badfunc3)(void);
__builtin_dump_struct(); // expected-error {{too few arguments to function call, expected 2, have 0}}
__builtin_dump_struct(1); // expected-error {{too few arguments to function call, expected 2, have 1}}
- __builtin_dump_struct(1, 2); // expected-error {{passing 'int' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('int' vs structure pointer)}}
- __builtin_dump_struct(&a, 2); // expected-error {{passing 'int' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(b, goodfunc); // expected-error {{passing 'void *' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('void *' vs structure pointer)}}
- __builtin_dump_struct(&a, badfunc1); // expected-error {{passing 'int (*)(const char *)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(const char *)' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(&a, badfunc2); // expected-error {{passing 'int (*)(int, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(int, ...)' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(&a, badfunc3); // expected-error {{passing 'void (*)(const char *, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('void (*)(const char *, ...)' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(&a, badfunc4); // expected-error {{passing 'int (*)(char *, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(char *, ...)' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(&a, badfunc5); // expected-error {{passing 'int (*)(void)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(void)' vs 'int (*)(const char *, ...)')}}
- __builtin_dump_struct(a, goodfunc); // expected-error {{passing 'struct A' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('struct A' vs structure pointer)}}
+ __builtin_dump_struct(1, 2); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'int'}}
+ __builtin_dump_struct(&a, 2); // expected-error {{expected a callable expression as 2nd argument to '__builtin_dump_struct', found 'int'}}
+ __builtin_dump_struct(b, goodfunc); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'void *'}}
+ __builtin_dump_struct(&a, badfunc1); // expected-error {{too many arguments to function call, expected 1, have 2}} expected-note {{in call to printing function with arguments '("%s", "struct A")'}}
+ __builtin_dump_struct(&a, badfunc2); // expected-warning-re 1+{{incompatible pointer to integer conversion passing 'char[{{.*}}]' to parameter of type 'int'}}
+ // expected-note at -1 1+{{in call to printing function with arguments '("}}
+ __builtin_dump_struct(&a, badfunc3); // expected-error {{too many arguments to function call, expected 0, have 2}} expected-note {{in call to printing function with arguments '("%s", "struct A")'}}
+ __builtin_dump_struct(a, goodfunc); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'struct A'}}
}
+int goodglobalfunc(const char*, ...);
+
void valid_uses(void) {
struct A {
};
@@ -33,10 +32,18 @@ void valid_uses(void) {
int (*goodfunc)(const char *, ...);
int (*goodfunc2)();
+ void (*goodfunc3)(const char *, ...);
+ int (*goodfunc4)(char *, ...);
+ int (^goodblock)(const char*, ...);
struct A a;
union B b;
+ __builtin_dump_struct(&a, goodglobalfunc);
+ __builtin_dump_struct(&a, &goodglobalfunc);
__builtin_dump_struct(&a, goodfunc);
__builtin_dump_struct(&b, goodfunc);
__builtin_dump_struct(&a, goodfunc2);
+ __builtin_dump_struct(&a, goodfunc3);
+ __builtin_dump_struct(&a, goodfunc4);
+ __builtin_dump_struct(&a, goodblock);
}
diff --git a/clang/test/SemaCXX/builtin-dump-struct.cpp b/clang/test/SemaCXX/builtin-dump-struct.cpp
new file mode 100644
index 000000000000..e057eac02946
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-dump-struct.cpp
@@ -0,0 +1,161 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+
+ template <class E> struct initializer_list {
+ const E *data;
+ size_t size;
+
+ constexpr initializer_list(const E *data, size_t size)
+ : data(data), size(size) {}
+ constexpr initializer_list() : data(), size() {}
+
+ constexpr const E *begin() const { return data; }
+ constexpr const E *end() const { return data + size; }
+ };
+}
+
+struct ConstexprString {
+ constexpr ConstexprString() : ConstexprString("") {}
+ constexpr ConstexprString(const char *p, std::size_t size) : data(new char[size+1]) {
+ __builtin_memcpy(data, p, size);
+ data[size] = '\0';
+ }
+ constexpr ConstexprString(const char *p) : ConstexprString(p, __builtin_strlen(p)) {}
+ constexpr explicit ConstexprString(const char *p, const char *q) : data(nullptr) {
+ auto p_size = __builtin_strlen(p);
+ auto q_size = __builtin_strlen(q);
+ data = new char[p_size + q_size + 1];
+ __builtin_memcpy(data, p, p_size);
+ __builtin_memcpy(data + p_size, q, q_size + 1);
+ }
+ constexpr ConstexprString(const ConstexprString &o) : ConstexprString(o.data) {}
+ constexpr ConstexprString(ConstexprString &&o) : data(o.data) { o.data = nullptr; }
+ constexpr ConstexprString &operator=(const ConstexprString &o) {
+ return *this = ConstexprString(o);
+ }
+ constexpr ConstexprString &operator=(ConstexprString &&o) {
+ delete[] data;
+ data = o.data;
+ o.data = nullptr;
+ return *this;
+ }
+ constexpr ~ConstexprString() { delete[] data; }
+ char *data;
+
+ friend constexpr ConstexprString operator+(const ConstexprString &a, const ConstexprString &b) {
+ return ConstexprString(a.data, b.data);
+ }
+ friend constexpr ConstexprString &operator+=(ConstexprString &a, const ConstexprString &b) {
+ return a = a + b;
+ }
+ friend constexpr bool operator==(const ConstexprString &a, const ConstexprString &b) {
+ return __builtin_strcmp(a.data, b.data) == 0;
+ }
+};
+
+template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args);
+
+struct Arg {
+ template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr>
+ constexpr Arg(T value) {
+ bool negative = false;
+ if (value < 0) {
+ value = -value;
+ negative = true;
+ }
+ while (value > 0) {
+ char str[2] = {char('0' + value % 10), '\0'};
+ s = ConstexprString(str) + s;
+ value /= 10;
+ }
+ if (negative)
+ s = "-" + s;
+ }
+ template<typename T, int (*)[__is_class(T) ? 1 : -1] = nullptr>
+ constexpr Arg(const T &value) {
+ __builtin_dump_struct(&value, Format, s);
+ }
+ constexpr Arg(const char *s) : s(s) {}
+ constexpr Arg(const ConstexprString *s) : s("\"" + *s + "\"") {}
+ template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr>
+ constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {}
+ ConstexprString s;
+};
+
+template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args) { // #Format
+ Arg formatted_args[] = {args...};
+ int i = 0;
+ while (const char *percent = __builtin_strchr(fmt, '%')) {
+ if (percent[1] == '%') continue;
+ if (percent != fmt && percent[-1] == '*') --percent;
+ out += ConstexprString(fmt, percent - fmt);
+ out += formatted_args[i++].s;
+
+ // Skip past format specifier until we hit a conversion specifier.
+ fmt = percent;
+ while (!__builtin_strchr("diouxXeEfFgGcsp", *fmt)) ++fmt;
+ // Skip the conversion specifier too. TODO: Check it's the right one.
+ ++fmt;
+ }
+ out += ConstexprString(fmt);
+}
+
+template<typename T> constexpr ConstexprString ToString(const T &t) { return Arg(t).s; }
+
+struct A {
+ int x, y, z : 3;
+ int : 4;
+ ConstexprString s;
+};
+struct B : A {
+ int p, q;
+ struct {
+ int anon1, anon2;
+ };
+ union {
+ int anon3;
+ };
+ struct {
+ int m;
+ } c;
+ int &&r;
+};
+
+#if PRINT_OUTPUT
+#include <stdio.h>
+int main() {
+ puts(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}).data);
+}
+#else
+static_assert(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}) == &R"(
+B {
+ A {
+ int x = 1
+ int y = 2
+ int z : 3 = 3
+ ConstexprString s = "hello"
+ }
+ int p = 4
+ int q = 5
+ int anon1 = 6
+ int anon2 = 7
+ int anon3 = 8
+ struct (unnamed) c = {
+ int m = 9
+ }
+ int && r = reference to 10
+}
+)"[1]);
+
+void errors(B b) {
+ __builtin_dump_struct(); // expected-error {{too few arguments to function call, expected 2, have 0}}
+ __builtin_dump_struct(1); // expected-error {{too few arguments to function call, expected 2, have 1}}
+ __builtin_dump_struct(1, 2); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'int'}}
+ __builtin_dump_struct(&b, 2); // expected-error {{expected a callable expression as 2nd argument to '__builtin_dump_struct', found 'int'}}
+ __builtin_dump_struct(&b, Format, 0); // expected-error {{no matching function for call to 'Format'}}
+ // expected-note at -1 {{in call to printing function with arguments '(0, "%s", "B")' while dumping struct}}
+ // expected-note@#Format {{no known conversion from 'int' to 'ConstexprString &' for 1st argument}}
+}
+#endif
More information about the cfe-commits
mailing list