On Wed, Feb 26, 2020 at 1:48 PM Aaron Ballman <aa...@aaronballman.com> wrote: > > On Tue, Feb 25, 2020 at 4:36 PM Richard Smith <rich...@metafoo.co.uk> wrote: > > > > It looks like we forgot to document this builtin. Can some documentation be > > added? > > If Paul doesn't get to it first, I can try to stub some out.
I added some basic documentation in f35f59ac36db3c5fe636f6899d98ac690126a4f7 ~Aaron > > ~Aaron > > > > > On Tue, 10 Apr 2018 at 15:01, Aaron Ballman via cfe-commits > > <cfe-commits@lists.llvm.org> wrote: > >> > >> Author: aaronballman > >> Date: Tue Apr 10 14:58:13 2018 > >> New Revision: 329762 > >> > >> URL: http://llvm.org/viewvc/llvm-project?rev=329762&view=rev > >> Log: > >> Introduce a new builtin, __builtin_dump_struct, that is useful for dumping > >> structure contents at runtime in circumstances where debuggers may not be > >> easily available (such as in kernel work). > >> > >> Patch by Paul Semel. > >> > >> Added: > >> cfe/trunk/test/CodeGen/dump-struct-builtin.c > >> cfe/trunk/test/Sema/builtin-dump-struct.c > >> Modified: > >> cfe/trunk/include/clang/Basic/Builtins.def > >> cfe/trunk/lib/CodeGen/CGBuiltin.cpp > >> cfe/trunk/lib/Sema/SemaChecking.cpp > >> > >> Modified: cfe/trunk/include/clang/Basic/Builtins.def > >> URL: > >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Builtins.def?rev=329762&r1=329761&r2=329762&view=diff > >> ============================================================================== > >> --- cfe/trunk/include/clang/Basic/Builtins.def (original) > >> +++ cfe/trunk/include/clang/Basic/Builtins.def Tue Apr 10 14:58:13 2018 > >> @@ -1374,6 +1374,7 @@ BUILTIN(__builtin_addressof, "v*v&", "nc > >> 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") > >> > >> // Safestack builtins > >> BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn") > >> > >> Modified: cfe/trunk/lib/CodeGen/CGBuiltin.cpp > >> URL: > >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBuiltin.cpp?rev=329762&r1=329761&r2=329762&view=diff > >> ============================================================================== > >> --- cfe/trunk/lib/CodeGen/CGBuiltin.cpp (original) > >> +++ cfe/trunk/lib/CodeGen/CGBuiltin.cpp Tue Apr 10 14:58:13 2018 > >> @@ -14,6 +14,7 @@ > >> #include "CGCXXABI.h" > >> #include "CGObjCRuntime.h" > >> #include "CGOpenCLRuntime.h" > >> +#include "CGRecordLayout.h" > >> #include "CodeGenFunction.h" > >> #include "CodeGenModule.h" > >> #include "ConstantEmitter.h" > >> @@ -930,6 +931,93 @@ EmitCheckedMixedSignMultiply(CodeGenFunc > >> return RValue::get(Overflow); > >> } > >> > >> +static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, > >> + Value *&RecordPtr, CharUnits Align, Value > >> *Func, > >> + int Lvl) { > >> + const auto *RT = RType->getAs<RecordType>(); > >> + ASTContext &Context = CGF.getContext(); > >> + RecordDecl *RD = RT->getDecl()->getDefinition(); > >> + ASTContext &Ctx = RD->getASTContext(); > >> + const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD); > >> + std::string Pad = std::string(Lvl * 4, ' '); > >> + > >> + Value *GString = > >> + CGF.Builder.CreateGlobalStringPtr(RType.getAsString() + " {\n"); > >> + Value *Res = CGF.Builder.CreateCall(Func, {GString}); > >> + > >> + static llvm::DenseMap<QualType, const char *> Types; > >> + if (Types.empty()) { > >> + Types[Context.CharTy] = "%c"; > >> + Types[Context.BoolTy] = "%d"; > >> + 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"; > >> + } > >> + > >> + for (const auto *FD : RD->fields()) { > >> + uint64_t Off = RL.getFieldOffset(FD->getFieldIndex()); > >> + Off = Ctx.toCharUnitsFromBits(Off).getQuantity(); > >> + > >> + Value *FieldPtr = RecordPtr; > >> + if (RD->isUnion()) > >> + FieldPtr = CGF.Builder.CreatePointerCast( > >> + FieldPtr, > >> CGF.ConvertType(Context.getPointerType(FD->getType()))); > >> + else > >> + FieldPtr = CGF.Builder.CreateStructGEP(CGF.ConvertType(RType), > >> FieldPtr, > >> + FD->getFieldIndex()); > >> + > >> + GString = CGF.Builder.CreateGlobalStringPtr( > >> + llvm::Twine(Pad) > >> + .concat(FD->getType().getAsString()) > >> + .concat(llvm::Twine(' ')) > >> + .concat(FD->getNameAsString()) > >> + .concat(" : ") > >> + .str()); > >> + Value *TmpRes = CGF.Builder.CreateCall(Func, {GString}); > >> + Res = CGF.Builder.CreateAdd(Res, TmpRes); > >> + > >> + QualType CanonicalType = > >> + FD->getType().getUnqualifiedType().getCanonicalType(); > >> + > >> + // We check whether we are in a recursive type > >> + if (CanonicalType->isRecordType()) { > >> + Value *TmpRes = > >> + dumpRecord(CGF, CanonicalType, FieldPtr, Align, Func, Lvl + 1); > >> + Res = CGF.Builder.CreateAdd(TmpRes, Res); > >> + continue; > >> + } > >> + > >> + // We try to determine the best format to print the current field > >> + llvm::Twine Format = Types.find(CanonicalType) == Types.end() > >> + ? Types[Context.VoidPtrTy] > >> + : Types[CanonicalType]; > >> + > >> + Address FieldAddress = Address(FieldPtr, Align); > >> + FieldPtr = CGF.Builder.CreateLoad(FieldAddress); > >> + > >> + // FIXME Need to handle bitfield here > >> + GString = CGF.Builder.CreateGlobalStringPtr( > >> + Format.concat(llvm::Twine('\n')).str()); > >> + TmpRes = CGF.Builder.CreateCall(Func, {GString, FieldPtr}); > >> + 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; > >> +} > >> + > >> RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, > >> unsigned BuiltinID, const > >> CallExpr *E, > >> ReturnValueSlot ReturnValue) { > >> @@ -1196,6 +1284,18 @@ RValue CodeGenFunction::EmitBuiltinExpr( > >> return RValue::get(ComplexVal.first); > >> } > >> > >> + case Builtin::BI__builtin_dump_struct: { > >> + 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); > >> + Value *Res = dumpRecord(*this, Arg0Type, RecordPtr, Arg0Align, Func, > >> 0); > >> + return RValue::get(Res); > >> + } > >> + > >> case Builtin::BI__builtin_cimag: > >> case Builtin::BI__builtin_cimagf: > >> case Builtin::BI__builtin_cimagl: > >> > >> Modified: cfe/trunk/lib/Sema/SemaChecking.cpp > >> URL: > >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=329762&r1=329761&r2=329762&view=diff > >> ============================================================================== > >> --- cfe/trunk/lib/Sema/SemaChecking.cpp (original) > >> +++ cfe/trunk/lib/Sema/SemaChecking.cpp Tue Apr 10 14:58:13 2018 > >> @@ -1105,6 +1105,63 @@ Sema::CheckBuiltinFunctionCall(FunctionD > >> 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->getLocStart(), > >> 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->getLocStart(), > >> diag::err_typecheck_convert_incompatible) > >> + << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3 > >> + << 2 << FnPtrArgType << "'int (*)(const char *, ...)'"; > >> + return ExprError(); > >> + } > >> + > >> + const auto *FuncType = > >> + FnPtrArgType->getPointeeType()->getAs<FunctionType>(); > >> + > >> + if (!FuncType) { > >> + Diag(FnPtrArg->getLocStart(), > >> diag::err_typecheck_convert_incompatible) > >> + << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3 > >> + << 2 << FnPtrArgType << "'int (*)(const char *, ...)'"; > >> + return ExprError(); > >> + } > >> + > >> + if (const auto *FT = dyn_cast<FunctionProtoType>(FuncType)) { > >> + if (!FT->getNumParams()) { > >> + Diag(FnPtrArg->getLocStart(), > >> 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->getLocStart(), > >> diag::err_typecheck_convert_incompatible) > >> + << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 > >> << 3 > >> + << 2 << FnPtrArgType << "'int (*)(const char *, ...)'"; > >> + return ExprError(); > >> + } > >> + } > >> + > >> + TheCall->setType(Context.IntTy); > >> + break; > >> + } > >> + > >> // check secure string manipulation functions where overflows > >> // are detectable at compile time > >> case Builtin::BI__builtin___memcpy_chk: > >> > >> Added: cfe/trunk/test/CodeGen/dump-struct-builtin.c > >> URL: > >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/dump-struct-builtin.c?rev=329762&view=auto > >> ============================================================================== > >> --- cfe/trunk/test/CodeGen/dump-struct-builtin.c (added) > >> +++ cfe/trunk/test/CodeGen/dump-struct-builtin.c Tue Apr 10 14:58:13 2018 > >> @@ -0,0 +1,391 @@ > >> +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | > >> FileCheck %s > >> + > >> +#include "Inputs/stdio.h" > >> + > >> +int printf(const char *fmt, ...) { > >> + return 0; > >> +} > >> + > >> +void unit1() { > >> + struct U1A { > >> + short a; > >> + }; > >> + > >> + struct U1A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U1A, > >> %struct.U1A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit2() { > >> + struct U2A { > >> + unsigned short a; > >> + }; > >> + > >> + struct U2A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U2A, > >> %struct.U2A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit3() { > >> + struct U3A { > >> + int a; > >> + }; > >> + > >> + struct U3A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U3A, > >> %struct.U3A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit4() { > >> + struct U4A { > >> + unsigned int a; > >> + }; > >> + > >> + struct U4A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U4A, > >> %struct.U4A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit5() { > >> + struct U5A { > >> + long a; > >> + }; > >> + > >> + struct U5A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U5A, > >> %struct.U5A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit6() { > >> + struct U6A { > >> + unsigned long a; > >> + }; > >> + > >> + struct U6A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U6A, > >> %struct.U6A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit7() { > >> + struct U7A { > >> + long long a; > >> + }; > >> + > >> + struct U7A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U7A, > >> %struct.U7A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit8() { > >> + struct U8A { > >> + long long a; > >> + }; > >> + > >> + struct U8A a = { > >> + .a = 12, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U8A, > >> %struct.U8A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit9() { > >> + struct U9A { > >> + char a; > >> + }; > >> + > >> + struct U9A a = { > >> + .a = 'a', > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U9A, > >> %struct.U9A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8, i8* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8 [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit10() { > >> + struct U10A { > >> + char *a; > >> + }; > >> + > >> + struct U10A a = { > >> + .a = "LSE", > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U10A, > >> %struct.U10A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit11() { > >> + struct U11A { > >> + void *a; > >> + }; > >> + > >> + struct U11A a = { > >> + .a = (void *)0x12345678, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U11A, > >> %struct.U11A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit12() { > >> + struct U12A { > >> + const char *a; > >> + }; > >> + > >> + struct U12A a = { > >> + .a = "LSE", > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U12A, > >> %struct.U12A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit13() { > >> + typedef char *charstar; > >> + struct U13A { > >> + const charstar a; > >> + }; > >> + > >> + struct U13A a = { > >> + .a = "LSE", > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U13A, > >> %struct.U13A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit14() { > >> + struct U14A { > >> + double a; > >> + }; > >> + > >> + struct U14A a = { > >> + .a = 1.123456, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U14A, > >> %struct.U14A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load double, double* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, double [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void unit15() { > >> + struct U15A { > >> + int a[3]; > >> + }; > >> + > >> + struct U15A a = { > >> + .a = {1, 2, 3}, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U15A, > >> %struct.U15A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load [3 x i32], [3 x i32]* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, [3 x i32] [[LOAD1]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void test1() { > >> + struct T1A { > >> + int a; > >> + char *b; > >> + }; > >> + > >> + struct T1A a = { > >> + .a = 12, > >> + .b = "LSE", > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T1A, > >> %struct.T1A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) > >> + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T1A, > >> %struct.T1A* %a, i32 0, i32 1 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD2:%[0-9]+]] = load i8*, i8** [[RES2]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD2]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void test2() { > >> + 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:%[0-9]+]] = getelementptr inbounds %struct.T2B, > >> %struct.T2B* %b, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) > >> + // CHECK: [[NESTED_STRUCT:%[0-9]+]] = getelementptr inbounds > >> %struct.T2B, %struct.T2B* %b, i32 0, i32 1 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T2A, > >> %struct.T2A* [[NESTED_STRUCT]], i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[LOAD2:%[0-9]+]] = load i32, i32* [[RES2]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD2]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&b, &printf); > >> +} > >> + > >> +void test3() { > >> + struct T3A { > >> + union { > >> + int a; > >> + char b[4]; > >> + }; > >> + }; > >> + > >> + struct T3A a = { > >> + .a = 42, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T3A, > >> %struct.T3A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon* [[RES1]] to i32* > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[BC1]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) > >> + // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon* [[RES1]] to [4 x i8]* > >> + // CHECK: [[LOAD2:%[0-9]+]] = load [4 x i8], [4 x i8]* [[BC2]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> + > >> +void test4() { > >> + struct T4A { > >> + union { > >> + struct { > >> + void *a; > >> + }; > >> + struct { > >> + unsigned long b; > >> + }; > >> + }; > >> + }; > >> + > >> + struct T4A a = { > >> + .a = (void *)0x12345678, > >> + }; > >> + > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T4A, > >> %struct.T4A* %a, i32 0, i32 0 > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to > >> %struct.anon* > >> + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.anon, > >> %struct.anon* [[BC1]], i32 0, i32 0 > >> + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES2]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) > >> + > >> + // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to > >> %struct.anon.1* > >> + // CHECK: [[RES3:%[0-9]+]] = getelementptr inbounds %struct.anon.1, > >> %struct.anon.1* [[BC2]], i32 0, i32 0 > >> + // CHECK: [[LOAD2:%[0-9]+]] = load i64, i64* [[RES3]], > >> + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD2]]) > >> + // CHECK: call i32 (i8*, ...) @printf( > >> + __builtin_dump_struct(&a, &printf); > >> +} > >> > >> Added: cfe/trunk/test/Sema/builtin-dump-struct.c > >> URL: > >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/builtin-dump-struct.c?rev=329762&view=auto > >> ============================================================================== > >> --- cfe/trunk/test/Sema/builtin-dump-struct.c (added) > >> +++ cfe/trunk/test/Sema/builtin-dump-struct.c Tue Apr 10 14:58:13 2018 > >> @@ -0,0 +1,42 @@ > >> +// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only > >> -fno-spell-checking -verify %s > >> + > >> +void invalid_uses() { > >> + struct A { > >> + }; > >> + struct A a; > >> + void *b; > >> + int (*goodfunc)(const char *, ...); > >> + int (*badfunc1)(const char *); > >> + int (*badfunc2)(int, ...); > >> + void (*badfunc3)(const char *, ...); > >> + int (*badfunc4)(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)}} > >> +} > >> + > >> +void valid_uses() { > >> + struct A { > >> + }; > >> + union B { > >> + }; > >> + > >> + int (*goodfunc)(const char *, ...); > >> + int (*goodfunc2)(); > >> + struct A a; > >> + union B b; > >> + > >> + __builtin_dump_struct(&a, goodfunc); > >> + __builtin_dump_struct(&b, goodfunc); > >> + __builtin_dump_struct(&a, goodfunc2); > >> +} > >> > >> > >> _______________________________________________ > >> cfe-commits mailing list > >> cfe-commits@lists.llvm.org > >> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits