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. ~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