From c4f95ef86a224fe730d2219aab90e88a0e7b03d2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 5 May 2022 14:52:57 -0700 Subject: [PATCH] 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 --- clang/docs/LanguageExtensions.rst | 86 ++- clang/docs/ReleaseNotes.rst | 11 +- clang/include/clang/Basic/Builtins.def | 2 +- clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 + clang/include/clang/Sema/Sema.h | 18 +- clang/lib/CodeGen/CGBuiltin.cpp | 130 ---- clang/lib/Frontend/FrontendActions.cpp | 2 + clang/lib/Sema/SemaChecking.cpp | 401 +++++++++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 23 + clang/test/CodeGen/builtin-dump-struct.c | 271 ++++++++ clang/test/CodeGen/dump-struct-builtin.c | 809 ----------------------- clang/test/CodeGenCXX/builtin-dump-struct.cpp | 125 ++++ clang/test/Sema/builtin-dump-struct.c | 33 +- clang/test/SemaCXX/builtin-dump-struct.cpp | 161 +++++ 14 files changed, 1028 insertions(+), 1050 deletions(-) create mode 100644 clang/test/CodeGen/builtin-dump-struct.c delete mode 100644 clang/test/CodeGen/dump-struct-builtin.c create mode 100644 clang/test/CodeGenCXX/builtin-dump-struct.cpp create mode 100644 clang/test/SemaCXX/builtin-dump-struct.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 426ca4a..bc90f9c 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 + 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 different 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 090b4c6b..47abde0 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 e86475b..ad55fdb 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 614bacc..2e1688d 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 7d33b50..44fbffd 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8897,6 +8897,10 @@ public: /// 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 @@ public: /// 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 @@ public: /// 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 caea5d1..a76677e 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()->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 &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 e219be3..10d6e1f 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -480,6 +480,8 @@ private: 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 18387c2..1ef8a6a 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 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 Exprs = {}) { + SmallVector 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 &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()) { + 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(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(D); + auto *FD = IFD ? IFD->getAnonField() : dyn_cast(D); + if (!FD || FD->isUnnamedBitfield() || FD->isAnonymousStructOrUnion()) + continue; + + llvm::SmallString<20> Format = llvm::StringRef("%s%s %s "); + llvm::SmallVector 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(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(); + 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(); - - 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(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 1143023..4fd9d00 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 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(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 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 0000000..af0a42c --- /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 + +// 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 730119c..0000000 --- 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 - -// 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 0000000..4f5f5b9 --- /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 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 d898bac..d0415a2 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@-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 0000000..e057eac --- /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 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 constexpr void Format(ConstexprString &out, const char *fmt, T... args); + +struct Arg { + template + 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 + 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 + constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {} + ConstexprString s; +}; + +template 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 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 +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@-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 -- 2.7.4