Introduce a new builtin, __builtin_dump_struct, that is useful for dumping structure...
authorAaron Ballman <aaron@aaronballman.com>
Tue, 10 Apr 2018 21:58:13 +0000 (21:58 +0000)
committerAaron Ballman <aaron@aaronballman.com>
Tue, 10 Apr 2018 21:58:13 +0000 (21:58 +0000)
Patch by Paul Semel.

llvm-svn: 329762

clang/include/clang/Basic/Builtins.def
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/Sema/SemaChecking.cpp
clang/test/CodeGen/dump-struct-builtin.c [new file with mode: 0644]
clang/test/Sema/builtin-dump-struct.c [new file with mode: 0644]

index a064a13..4b74662 100644 (file)
@@ -1374,6 +1374,7 @@ BUILTIN(__builtin_addressof, "v*v&", "nct")
 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")
index 2be9b78..73b95d9 100644 (file)
@@ -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(CodeGenFunction &CGF, const clang::Expr *Op1,
   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(const FunctionDecl *FD,
     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:
index fdf7e2b..70e98e2 100644 (file)
@@ -1105,6 +1105,63 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
       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:
diff --git a/clang/test/CodeGen/dump-struct-builtin.c b/clang/test/CodeGen/dump-struct-builtin.c
new file mode 100644 (file)
index 0000000..ca8c96e
--- /dev/null
@@ -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);
+}
diff --git a/clang/test/Sema/builtin-dump-struct.c b/clang/test/Sema/builtin-dump-struct.c
new file mode 100644 (file)
index 0000000..ec82ad1
--- /dev/null
@@ -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);
+}