r329762 - 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).
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 26 10:48:34 PST 2020
On Tue, Feb 25, 2020 at 4:36 PM Richard Smith <richard at 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 at 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 at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
More information about the cfe-commits
mailing list