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

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 25 13:36:00 PST 2020


It looks like we forgot to document this builtin. Can some documentation be
added?

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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200225/1810e3e5/attachment-0001.html>


More information about the cfe-commits mailing list