diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2368,44 +2368,82 @@ .. 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 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -171,8 +171,15 @@ - 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. New Compiler Flags ------------------ diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1602,7 +1602,7 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8467,6 +8467,12 @@ 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8895,6 +8895,10 @@ /// 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 @@ -8916,9 +8920,14 @@ /// 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. @@ -8926,6 +8935,9 @@ /// 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/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2457,8 +2457,12 @@ } // Objective-C++ extensions to the rule. - if (isa(E) || isa(E)) + if (isa(E)) return true; + if (const auto *POE = dyn_cast(E)) { + if (isa(POE->getSyntacticForm())) + return true; + } return false; } @@ -2708,23 +2712,35 @@ } case ObjCPropertyRefExprClass: + case ObjCSubscriptRefExprClass: WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; case PseudoObjectExprClass: { - const PseudoObjectExpr *PO = cast(this); + const auto *POE = cast(this); - // Only complain about things that have the form of a getter. - if (isa(PO->getSyntacticForm()) || - isa(PO->getSyntacticForm())) - return false; + // For some syntactic forms, we should always warn. + if (isa( + POE->getSyntacticForm())) { + WarnE = this; + Loc = getExprLoc(); + R1 = getSourceRange(); + return true; + } - WarnE = this; - Loc = getExprLoc(); - R1 = getSourceRange(); - return true; + // For others, we should never warn. + if (auto *BO = dyn_cast(POE->getSyntacticForm())) + if (BO->isAssignmentOp()) + return false; + if (auto *UO = dyn_cast(POE->getSyntacticForm())) + if (UO->isIncrementDecrementOp()) + return false; + + // Otherwise, warn if the result expression would warn. + const Expr *Result = POE->getResultExpr(); + return Result && Result->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } case StmtExprClass: { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2043,106 +2043,6 @@ return RValue::get(Overflow); } -static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, - LValue RecordLV, CharUnits Align, - llvm::FunctionCallee Func, int Lvl) { - ASTContext &Context = CGF.getContext(); - RecordDecl *RD = RType->castAs()->getDecl()->getDefinition(); - std::string Pad = std::string(Lvl * 4, ' '); - std::string ElementPad = std::string((Lvl + 1) * 4, ' '); - - PrintingPolicy Policy(Context.getLangOpts()); - Policy.AnonymousTagLocations = false; - Value *GString = CGF.Builder.CreateGlobalStringPtr( - llvm::Twine(Pad).concat(RType.getAsString(Policy)).concat(" {\n").str()); - Value *Res = CGF.Builder.CreateCall(Func, {GString}); - - static llvm::DenseMap Types; - if (Types.empty()) { - Types[Context.CharTy] = "%c"; - Types[Context.BoolTy] = "%d"; - Types[Context.SignedCharTy] = "%hhd"; - Types[Context.UnsignedCharTy] = "%hhu"; - Types[Context.IntTy] = "%d"; - Types[Context.UnsignedIntTy] = "%u"; - Types[Context.LongTy] = "%ld"; - Types[Context.UnsignedLongTy] = "%lu"; - Types[Context.LongLongTy] = "%lld"; - Types[Context.UnsignedLongLongTy] = "%llu"; - Types[Context.ShortTy] = "%hd"; - Types[Context.UnsignedShortTy] = "%hu"; - Types[Context.VoidPtrTy] = "%p"; - Types[Context.FloatTy] = "%f"; - Types[Context.DoubleTy] = "%f"; - Types[Context.LongDoubleTy] = "%Lf"; - Types[Context.getPointerType(Context.CharTy)] = "%s"; - Types[Context.getPointerType(Context.getConstType(Context.CharTy))] = "%s"; - } - - for (const auto *FD : RD->fields()) { - Value *TmpRes = nullptr; - - std::string Format = llvm::Twine(ElementPad) - .concat(FD->getType().getAsString()) - .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; - } - } - - LValue FieldLV = CGF.EmitLValueForField(RecordLV, FD); - QualType CanonicalType = - FD->getType().getUnqualifiedType().getCanonicalType(); - - // We check whether we are in a recursive type - if (CanonicalType->isRecordType()) { - TmpRes = dumpRecord(CGF, CanonicalType, FieldLV, Align, Func, Lvl + 1); - Res = CGF.Builder.CreateAdd(TmpRes, Res); - continue; - } - - // We try to determine the best format to print the current field - const char *TypeFormat = Types.find(CanonicalType) == Types.end() - ? Types[Context.VoidPtrTy] - : Types[CanonicalType]; - - GString = CGF.Builder.CreateGlobalStringPtr(llvm::Twine(Format) - .concat(" = ") - .concat(TypeFormat) - .concat(llvm::Twine('\n')) - .str()); - - RValue RV = FD->isBitField() - ? CGF.EmitLoadOfBitfieldLValue(FieldLV, FD->getLocation()) - : CGF.EmitLoadOfLValue(FieldLV, FD->getLocation()); - 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 bool TypeRequiresBuiltinLaunderImp(const ASTContext &Ctx, QualType Ty, llvm::SmallPtrSetImpl &Seen) { @@ -2669,24 +2569,6 @@ 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}, 0); - 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 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -480,6 +480,8 @@ 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 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -366,6 +366,291 @@ return false; } +namespace { +struct BuiltinDumpStructGenerator { + Sema &S; + CallExpr *TheCall; + SourceLocation Loc = TheCall->getBeginLoc(); + SmallVector Actions; + DiagnosticErrorTrap ErrorTracker; + + BuiltinDumpStructGenerator(Sema &S, CallExpr *TheCall) + : S(S), TheCall(TheCall), ErrorTracker(S.getDiagnostics()) {} + + 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; + 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 * 4, ' '); + return getStringLiteral(Indent); + } + + Expr *getTypeString(QualType T) { return getStringLiteral(T.getAsString()); } + + llvm::StringRef getFormatSpecifier(QualType T) { + if (auto *BT = T->getAs()) { + switch (BT->getKind()) { + case BuiltinType::Bool: + return "%d"; + case BuiltinType::Char_U: + return "%c"; + case BuiltinType::Char_S: + return "%c"; + case BuiltinType::UChar: + return "%hhu"; + case BuiltinType::SChar: + return "%hhd"; + case BuiltinType::Short: + return "%hd"; + case BuiltinType::UShort: + return "%hu"; + case BuiltinType::Int: + return "%d"; + case BuiltinType::UInt: + return "%u"; + case BuiltinType::Long: + return "%ld"; + case BuiltinType::ULong: + return "%lu"; + case BuiltinType::LongLong: + return "%lld"; + case BuiltinType::ULongLong: + return "%llu"; + case BuiltinType::Float: + return "%f"; + case BuiltinType::Double: + return "%f"; + case BuiltinType::LongDouble: + return "%Lf"; + default: + break; + } + } + + if (T->isPointerType()) { + QualType Pointee = T->getPointeeType(); + if (S.Context.hasSameType(Pointee, S.Context.CharTy) || + S.Context.hasSameType(Pointee, + S.Context.getConstType(S.Context.CharTy))) + return "%s"; + return "%p"; + } + + return ""; + } + + 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) { + // Build an OpaqueValueExpr so we can refer to E more than once without + // triggering re-evaluation. + auto *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 CXXRecordDecl *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, getStringLiteral(FD->getType().getAsString()), + 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 = InnerRD ? dyn_cast(InnerRD) : nullptr; + 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 { + llvm::StringRef FieldFormat = getFormatSpecifier(FD->getType()); + if (!FieldFormat.empty()) { + // We know how to print this field. Do it. + Format += " "; + Format += FieldFormat; + 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 (TheCall->getNumArgs() < 2 && checkArgCount(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() && + !(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); + + if (Generator.dumpUnnamedRecord(RD, PtrArgResult.get(), 0)) + return ExprError(); + + return Generator.buildWrapper(); +} + static bool SemaBuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) { if (checkArgCount(S, BuiltinCall, 2)) return true; @@ -2013,62 +2298,8 @@ 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/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -341,7 +341,7 @@ return DiagnoseUnusedExprResult(POE->getSemanticExpr(0), DiagID); if (isa(Source)) DiagID = diag::warn_unused_container_subscript_expr; - else + else if (isa(Source)) DiagID = diag::warn_unused_property_expr; } else if (const CXXFunctionalCastExpr *FC = dyn_cast(E)) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -213,6 +213,7 @@ 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 @@ } } +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 @@ << 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 @@ 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 --- /dev/null +++ b/clang/test/CodeGen/builtin-dump-struct.c @@ -0,0 +1,272 @@ +// 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_3:.*]] = private unnamed_addr constant [14 x i8] c"%s%s %s = %c\0A\00", +// CHECK-DAG: @[[STR_4:.*]] = private unnamed_addr constant [5 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 [14 x i8] c"%s%s %s = %s\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 [9 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_3]], 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 --- a/clang/test/CodeGen/dump-struct-builtin.c +++ /dev/null @@ -1,639 +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 [14 x i8] c"struct U1A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U1:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" short a = %hd\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U2A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U2:@[0-9]+]] = private unnamed_addr constant [28 x i8] c" unsigned short a = %hu\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U3A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U3:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" int a = %d\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U4A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U4:@[0-9]+]] = private unnamed_addr constant [25 x i8] c" unsigned int a = %u\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U5A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U5:@[0-9]+]] = private unnamed_addr constant [18 x i8] c" long a = %ld\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U6A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U6:@[0-9]+]] = private unnamed_addr constant [27 x i8] c" unsigned long a = %lu\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U7A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U7:@[0-9]+]] = private unnamed_addr constant [24 x i8] c" long long a = %lld\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U8A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U8:@[0-9]+]] = private unnamed_addr constant [33 x i8] c" unsigned long long a = %llu\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [14 x i8] c"struct U9A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U9:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" char a = %c\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U10A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U10:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" char * a = %s\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U11A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U11:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" void * a = %p\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U12A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U12:@[0-9]+]] = private unnamed_addr constant [25 x i8] c" const char * a = %s\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U13A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U13:@[0-9]+]] = private unnamed_addr constant [27 x i8] c" const charstar a = %s\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U14A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U14:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" double a = %f\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U15A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" int[3] a = %p\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U16A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U16:@[0-9]+]] = private unnamed_addr constant [22 x i8] c" uint8_t a = %hhu\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U17A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U17:@[0-9]+]] = private unnamed_addr constant [21 x i8] c" int8_t a = %hhd\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_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 [15 x i8] c"struct U18A {\0A\00", align 1 -// CHECK-NEXT: [[FIELD_U18:@[0-9]+]] = private unnamed_addr constant [25 x i8] c" long double a = %Lf\0A\00", align 1 -// CHECK-NEXT: [[END_STRUCT_U18:@[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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([19 x i8], [19 x i8]* [[FIELD_U1]], i32 0, i32 0), i16 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([28 x i8], [28 x i8]* [[FIELD_U2]], i32 0, i32 0), i16 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([16 x i8], [16 x i8]* [[FIELD_U3]], i32 0, i32 0), i32 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([25 x i8], [25 x i8]* [[FIELD_U4]], i32 0, i32 0), i32 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([18 x i8], [18 x i8]* [[FIELD_U5]], i32 0, i32 0), i64 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([27 x i8], [27 x i8]* [[FIELD_U6]], i32 0, i32 0), i64 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([24 x i8], [24 x i8]* [[FIELD_U7]], i32 0, i32 0), i64 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([33 x i8], [33 x i8]* [[FIELD_U8]], i32 0, i32 0), i64 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([14 x i8], [14 x i8]* [[STRUCT_STR_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 ([17 x i8], [17 x i8]* [[FIELD_U9]], i32 0, i32 0), i8 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([19 x i8], [19 x i8]* [[FIELD_U10]], i32 0, i32 0), i8* [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([19 x i8], [19 x i8]* [[FIELD_U11]], i32 0, i32 0), i8* [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([25 x i8], [25 x i8]* [[FIELD_U12]], i32 0, i32 0), i8* [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([27 x i8], [27 x i8]* [[FIELD_U13]], i32 0, i32 0), i8* [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([19 x i8], [19 x i8]* [[FIELD_U14]], i32 0, i32 0), double [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([19 x i8], [19 x i8]* [[FIELD_U15]], i32 0, i32 0), [3 x i32] [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([22 x i8], [22 x i8]* [[FIELD_U16]], i32 0, i32 0), i8 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([21 x i8], [21 x i8]* [[FIELD_U17]], i32 0, i32 0), i8 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_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 ([15 x i8], [15 x i8]* [[STRUCT_STR_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 ([25 x i8], [25 x i8]* [[FIELD_U18]], i32 0, i32 0), x86_fp80 [[LOAD1]]) - // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U18]], 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: [[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: [[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: [[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: [[NESTED_STRUCT:%.*]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 1 - // 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( - __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: [[RES1:%.*]] = getelementptr inbounds %struct.T3A, %struct.T3A* %a, i32 0, i32 0 - // 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: [[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: [[RES1:%.*]] = getelementptr inbounds %struct.T4A, %struct.T4A* %a, i32 0, i32 0 - // CHECK: call i32 (i8*, ...) @printf( - // CHECK: [[BC1:%.*]] = bitcast %union.anon.0* [[RES1]] to %struct.anon* - // 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: [[BC2:%.*]] = bitcast %union.anon.0* [[RES1]] to %struct.anon.1* - // 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(i8* getelementptr inbounds ([14 x i8], [14 x i8]* {{.*}}, i32 0, i32 0)) - // 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 ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), 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: [[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 ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST1]]) - // 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 ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST2]]) - // 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 ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), 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: [[RES1:%.*]] = getelementptr inbounds %struct.T7B, %struct.T7B* %a, i32 0, i32 0 - // 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 ([33 x i8], [33 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST1]]) - // 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 ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), 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: [[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(i8* getelementptr inbounds ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST1]]) - // 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(i8* getelementptr inbounds ([27 x i8], [27 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST2]]) - // 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 ([25 x i8], [25 x i8]* {{.*}}, i32 0, i32 0), 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 --- /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:.*]] = {{.*}} [9 x i8] c"struct 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:.*]] = {{.*}} [5 x i8] c" \00", +// CHECK-DAG: @[[STR_5:.*]] = {{.*}} [9 x i8] c"struct A\00", +// CHECK-DAG: @[[STR_6:.*]] = {{.*}} [14 x i8] c"%s%s %s = %d\0A\00", +// CHECK-DAG: @[[STR_7:.*]] = {{.*}} [9 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:.*]] = {{.*}} [9 x i8] c"struct 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:.*]] = {{.*}} [8 x i8] c"class 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 --- a/clang/test/Sema/builtin-dump-struct.c +++ b/clang/test/Sema/builtin-dump-struct.c @@ -8,21 +8,22 @@ int (*goodfunc)(const char *, ...); int (*badfunc1)(const char *); int (*badfunc2)(int, ...); - void (*badfunc3)(const char *, ...); - int (*badfunc4)(char *, ...); + void (*goodfunc3)(const char *, ...); + int (*goodfunc4)(char *, ...); int (*badfunc5)(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, goodfunc3); // OK + __builtin_dump_struct(&a, goodfunc4); // OK + __builtin_dump_struct(&a, badfunc5); // 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'}} } void valid_uses(void) { diff --git a/clang/test/SemaCXX/builtin-dump-struct.cpp b/clang/test/SemaCXX/builtin-dump-struct.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/builtin-dump-struct.cpp @@ -0,0 +1,135 @@ +// 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(T value) { + __builtin_dump_struct(&value, Format, s); + } + constexpr Arg(const char *s) : s(s) {} + constexpr Arg(const ConstexprString *s) : s("\"" + *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(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; }; }; + +extern "C" int puts(const char*); +int main() { puts(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8}).data); } + +static_assert(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8}) == &R"( +struct B { + struct A { + int x = 1 + int y = 2 + int z : 3 = 3 + struct ConstexprString s = "hello" + } + int p = 4 + int q = 5 + int anon1 = 6 + int anon2 = 7 + int anon3 = 8 +} +)"[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", "struct B")' while dumping struct}} + // expected-note@#Format {{no known conversion from 'int' to 'ConstexprString &' for 1st argument}} +}