<div dir="ltr">It looks like we forgot to document this builtin. Can some documentation be added?</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 10 Apr 2018 at 15:01, Aaron Ballman via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: aaronballman<br>
Date: Tue Apr 10 14:58:13 2018<br>
New Revision: 329762<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=329762&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=329762&view=rev</a><br>
Log:<br>
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).<br>
<br>
Patch by Paul Semel.<br>
<br>
Added:<br>
    cfe/trunk/test/CodeGen/dump-struct-builtin.c<br>
    cfe/trunk/test/Sema/builtin-dump-struct.c<br>
Modified:<br>
    cfe/trunk/include/clang/Basic/Builtins.def<br>
    cfe/trunk/lib/CodeGen/CGBuiltin.cpp<br>
    cfe/trunk/lib/Sema/SemaChecking.cpp<br>
<br>
Modified: cfe/trunk/include/clang/Basic/Builtins.def<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Builtins.def?rev=329762&r1=329761&r2=329762&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Builtins.def?rev=329762&r1=329761&r2=329762&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/Builtins.def (original)<br>
+++ cfe/trunk/include/clang/Basic/Builtins.def Tue Apr 10 14:58:13 2018<br>
@@ -1374,6 +1374,7 @@ BUILTIN(__builtin_addressof, "v*v&", "nc<br>
 BUILTIN(__builtin_operator_new, "v*z", "tc")<br>
 BUILTIN(__builtin_operator_delete, "vv*", "tn")<br>
 BUILTIN(__builtin_char_memchr, "c*cC*iz", "n")<br>
+BUILTIN(__builtin_dump_struct, "ivC*v*", "tn")<br>
<br>
 // Safestack builtins<br>
 BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn")<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGBuiltin.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBuiltin.cpp?rev=329762&r1=329761&r2=329762&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBuiltin.cpp?rev=329762&r1=329761&r2=329762&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGBuiltin.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGBuiltin.cpp Tue Apr 10 14:58:13 2018<br>
@@ -14,6 +14,7 @@<br>
 #include "CGCXXABI.h"<br>
 #include "CGObjCRuntime.h"<br>
 #include "CGOpenCLRuntime.h"<br>
+#include "CGRecordLayout.h"<br>
 #include "CodeGenFunction.h"<br>
 #include "CodeGenModule.h"<br>
 #include "ConstantEmitter.h"<br>
@@ -930,6 +931,93 @@ EmitCheckedMixedSignMultiply(CodeGenFunc<br>
   return RValue::get(Overflow);<br>
 }<br>
<br>
+static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType,<br>
+                               Value *&RecordPtr, CharUnits Align, Value *Func,<br>
+                               int Lvl) {<br>
+  const auto *RT = RType->getAs<RecordType>();<br>
+  ASTContext &Context = CGF.getContext();<br>
+  RecordDecl *RD = RT->getDecl()->getDefinition();<br>
+  ASTContext &Ctx = RD->getASTContext();<br>
+  const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD);<br>
+  std::string Pad = std::string(Lvl * 4, ' ');<br>
+<br>
+  Value *GString =<br>
+      CGF.Builder.CreateGlobalStringPtr(RType.getAsString() + " {\n");<br>
+  Value *Res = CGF.Builder.CreateCall(Func, {GString});<br>
+<br>
+  static llvm::DenseMap<QualType, const char *> Types;<br>
+  if (Types.empty()) {<br>
+    Types[Context.CharTy] = "%c";<br>
+    Types[Context.BoolTy] = "%d";<br>
+    Types[Context.IntTy] = "%d";<br>
+    Types[Context.UnsignedIntTy] = "%u";<br>
+    Types[Context.LongTy] = "%ld";<br>
+    Types[Context.UnsignedLongTy] = "%lu";<br>
+    Types[Context.LongLongTy] = "%lld";<br>
+    Types[Context.UnsignedLongLongTy] = "%llu";<br>
+    Types[Context.ShortTy] = "%hd";<br>
+    Types[Context.UnsignedShortTy] = "%hu";<br>
+    Types[Context.VoidPtrTy] = "%p";<br>
+    Types[Context.FloatTy] = "%f";<br>
+    Types[Context.DoubleTy] = "%f";<br>
+    Types[Context.LongDoubleTy] = "%Lf";<br>
+    Types[Context.getPointerType(Context.CharTy)] = "%s";<br>
+  }<br>
+<br>
+  for (const auto *FD : RD->fields()) {<br>
+    uint64_t Off = RL.getFieldOffset(FD->getFieldIndex());<br>
+    Off = Ctx.toCharUnitsFromBits(Off).getQuantity();<br>
+<br>
+    Value *FieldPtr = RecordPtr;<br>
+    if (RD->isUnion())<br>
+      FieldPtr = CGF.Builder.CreatePointerCast(<br>
+          FieldPtr, CGF.ConvertType(Context.getPointerType(FD->getType())));<br>
+    else<br>
+      FieldPtr = CGF.Builder.CreateStructGEP(CGF.ConvertType(RType), FieldPtr,<br>
+                                             FD->getFieldIndex());<br>
+<br>
+    GString = CGF.Builder.CreateGlobalStringPtr(<br>
+        llvm::Twine(Pad)<br>
+            .concat(FD->getType().getAsString())<br>
+            .concat(llvm::Twine(' '))<br>
+            .concat(FD->getNameAsString())<br>
+            .concat(" : ")<br>
+            .str());<br>
+    Value *TmpRes = CGF.Builder.CreateCall(Func, {GString});<br>
+    Res = CGF.Builder.CreateAdd(Res, TmpRes);<br>
+<br>
+    QualType CanonicalType =<br>
+        FD->getType().getUnqualifiedType().getCanonicalType();<br>
+<br>
+    // We check whether we are in a recursive type<br>
+    if (CanonicalType->isRecordType()) {<br>
+      Value *TmpRes =<br>
+          dumpRecord(CGF, CanonicalType, FieldPtr, Align, Func, Lvl + 1);<br>
+      Res = CGF.Builder.CreateAdd(TmpRes, Res);<br>
+      continue;<br>
+    }<br>
+<br>
+    // We try to determine the best format to print the current field<br>
+    llvm::Twine Format = Types.find(CanonicalType) == Types.end()<br>
+                             ? Types[Context.VoidPtrTy]<br>
+                             : Types[CanonicalType];<br>
+<br>
+    Address FieldAddress = Address(FieldPtr, Align);<br>
+    FieldPtr = CGF.Builder.CreateLoad(FieldAddress);<br>
+<br>
+    // FIXME Need to handle bitfield here<br>
+    GString = CGF.Builder.CreateGlobalStringPtr(<br>
+        Format.concat(llvm::Twine('\n')).str());<br>
+    TmpRes = CGF.Builder.CreateCall(Func, {GString, FieldPtr});<br>
+    Res = CGF.Builder.CreateAdd(Res, TmpRes);<br>
+  }<br>
+<br>
+  GString = CGF.Builder.CreateGlobalStringPtr(Pad + "}\n");<br>
+  Value *TmpRes = CGF.Builder.CreateCall(Func, {GString});<br>
+  Res = CGF.Builder.CreateAdd(Res, TmpRes);<br>
+  return Res;<br>
+}<br>
+<br>
 RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,<br>
                                         unsigned BuiltinID, const CallExpr *E,<br>
                                         ReturnValueSlot ReturnValue) {<br>
@@ -1196,6 +1284,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(<br>
     return RValue::get(ComplexVal.first);<br>
   }<br>
<br>
+  case Builtin::BI__builtin_dump_struct: {<br>
+    Value *Func = EmitScalarExpr(E->getArg(1)->IgnoreImpCasts());<br>
+    CharUnits Arg0Align = EmitPointerWithAlignment(E->getArg(0)).getAlignment();<br>
+<br>
+    const Expr *Arg0 = E->getArg(0)->IgnoreImpCasts();<br>
+    QualType Arg0Type = Arg0->getType()->getPointeeType();<br>
+<br>
+    Value *RecordPtr = EmitScalarExpr(Arg0);<br>
+    Value *Res = dumpRecord(*this, Arg0Type, RecordPtr, Arg0Align, Func, 0);<br>
+    return RValue::get(Res);<br>
+  }<br>
+<br>
   case Builtin::BI__builtin_cimag:<br>
   case Builtin::BI__builtin_cimagf:<br>
   case Builtin::BI__builtin_cimagl:<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaChecking.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=329762&r1=329761&r2=329762&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=329762&r1=329761&r2=329762&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Tue Apr 10 14:58:13 2018<br>
@@ -1105,6 +1105,63 @@ Sema::CheckBuiltinFunctionCall(FunctionD<br>
       CorrectDelayedTyposInExpr(TheCallResult.get());<br>
     return Res;<br>
   }<br>
+  case Builtin::BI__builtin_dump_struct: {<br>
+    // We first want to ensure we are called with 2 arguments<br>
+    if (checkArgCount(*this, TheCall, 2))<br>
+      return ExprError();<br>
+    // Ensure that the first argument is of type 'struct XX *'<br>
+    const Expr *PtrArg = TheCall->getArg(0)->IgnoreParenImpCasts();<br>
+    const QualType PtrArgType = PtrArg->getType();<br>
+    if (!PtrArgType->isPointerType() ||<br>
+        !PtrArgType->getPointeeType()->isRecordType()) {<br>
+      Diag(PtrArg->getLocStart(), diag::err_typecheck_convert_incompatible)<br>
+          << PtrArgType << "structure pointer" << 1 << 0 << 3 << 1 << PtrArgType<br>
+          << "structure pointer";<br>
+      return ExprError();<br>
+    }<br>
+<br>
+    // Ensure that the second argument is of type 'FunctionType'<br>
+    const Expr *FnPtrArg = TheCall->getArg(1)->IgnoreImpCasts();<br>
+    const QualType FnPtrArgType = FnPtrArg->getType();<br>
+    if (!FnPtrArgType->isPointerType()) {<br>
+      Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible)<br>
+          << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3<br>
+          << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";<br>
+      return ExprError();<br>
+    }<br>
+<br>
+    const auto *FuncType =<br>
+        FnPtrArgType->getPointeeType()->getAs<FunctionType>();<br>
+<br>
+    if (!FuncType) {<br>
+      Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible)<br>
+          << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3<br>
+          << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";<br>
+      return ExprError();<br>
+    }<br>
+<br>
+    if (const auto *FT = dyn_cast<FunctionProtoType>(FuncType)) {<br>
+      if (!FT->getNumParams()) {<br>
+        Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible)<br>
+            << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3<br>
+            << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";<br>
+        return ExprError();<br>
+      }<br>
+      QualType PT = FT->getParamType(0);<br>
+      if (!FT->isVariadic() || FT->getReturnType() != Context.IntTy ||<br>
+          !PT->isPointerType() || !PT->getPointeeType()->isCharType() ||<br>
+          !PT->getPointeeType().isConstQualified()) {<br>
+        Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible)<br>
+            << FnPtrArgType << "'int (*)(const char *, ...)'" << 1 << 0 << 3<br>
+            << 2 << FnPtrArgType << "'int (*)(const char *, ...)'";<br>
+        return ExprError();<br>
+      }<br>
+    }<br>
+<br>
+    TheCall->setType(Context.IntTy);<br>
+    break;<br>
+  }<br>
+<br>
   // check secure string manipulation functions where overflows<br>
   // are detectable at compile time<br>
   case Builtin::BI__builtin___memcpy_chk:<br>
<br>
Added: cfe/trunk/test/CodeGen/dump-struct-builtin.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/dump-struct-builtin.c?rev=329762&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/dump-struct-builtin.c?rev=329762&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/dump-struct-builtin.c (added)<br>
+++ cfe/trunk/test/CodeGen/dump-struct-builtin.c Tue Apr 10 14:58:13 2018<br>
@@ -0,0 +1,391 @@<br>
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s<br>
+<br>
+#include "Inputs/stdio.h"<br>
+<br>
+int printf(const char *fmt, ...) {<br>
+    return 0;<br>
+}<br>
+<br>
+void unit1() {<br>
+  struct U1A {<br>
+    short a;<br>
+  };<br>
+<br>
+  struct U1A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit2() {<br>
+  struct U2A {<br>
+    unsigned short a;<br>
+  };<br>
+<br>
+  struct U2A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit3() {<br>
+  struct U3A {<br>
+    int a;<br>
+  };<br>
+<br>
+  struct U3A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit4() {<br>
+  struct U4A {<br>
+    unsigned int a;<br>
+  };<br>
+<br>
+  struct U4A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit5() {<br>
+  struct U5A {<br>
+    long a;<br>
+  };<br>
+<br>
+  struct U5A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit6() {<br>
+  struct U6A {<br>
+    unsigned long a;<br>
+  };<br>
+<br>
+  struct U6A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit7() {<br>
+  struct U7A {<br>
+    long long a;<br>
+  };<br>
+<br>
+  struct U7A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit8() {<br>
+  struct U8A {<br>
+    long long a;<br>
+  };<br>
+<br>
+  struct U8A a = {<br>
+      .a = 12,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit9() {<br>
+  struct U9A {<br>
+    char a;<br>
+  };<br>
+<br>
+  struct U9A a = {<br>
+      .a = 'a',<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8, i8* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8 [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit10() {<br>
+  struct U10A {<br>
+    char *a;<br>
+  };<br>
+<br>
+  struct U10A a = {<br>
+      .a = "LSE",<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit11() {<br>
+  struct U11A {<br>
+    void *a;<br>
+  };<br>
+<br>
+  struct U11A a = {<br>
+      .a = (void *)0x12345678,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit12() {<br>
+  struct U12A {<br>
+    const char *a;<br>
+  };<br>
+<br>
+  struct U12A a = {<br>
+      .a = "LSE",<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U12A, %struct.U12A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit13() {<br>
+  typedef char *charstar;<br>
+  struct U13A {<br>
+    const charstar a;<br>
+  };<br>
+<br>
+  struct U13A a = {<br>
+      .a = "LSE",<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U13A, %struct.U13A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit14() {<br>
+  struct U14A {<br>
+    double a;<br>
+  };<br>
+<br>
+  struct U14A a = {<br>
+      .a = 1.123456,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U14A, %struct.U14A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load double, double* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, double [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void unit15() {<br>
+  struct U15A {<br>
+    int a[3];<br>
+  };<br>
+<br>
+  struct U15A a = {<br>
+      .a = {1, 2, 3},<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load [3 x i32], [3 x i32]* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, [3 x i32] [[LOAD1]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void test1() {<br>
+  struct T1A {<br>
+    int a;<br>
+    char *b;<br>
+  };<br>
+<br>
+  struct T1A a = {<br>
+      .a = 12,<br>
+      .b = "LSE",<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])<br>
+  // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 1<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD2:%[0-9]+]] = load i8*, i8** [[RES2]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD2]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void test2() {<br>
+  struct T2A {<br>
+    int a;<br>
+  };<br>
+<br>
+  struct T2B {<br>
+    int b;<br>
+    struct T2A a;<br>
+  };<br>
+<br>
+  struct T2B b = {<br>
+      .b = 24,<br>
+      .a = {<br>
+          .a = 12,<br>
+      }<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])<br>
+  // CHECK: [[NESTED_STRUCT:%[0-9]+]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 1<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T2A, %struct.T2A* [[NESTED_STRUCT]], i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[LOAD2:%[0-9]+]] = load i32, i32* [[RES2]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD2]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&b, &printf);<br>
+}<br>
+<br>
+void test3() {<br>
+  struct T3A {<br>
+    union {<br>
+      int a;<br>
+      char b[4];<br>
+    };<br>
+  };<br>
+<br>
+  struct T3A a = {<br>
+      .a = 42,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T3A, %struct.T3A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon* [[RES1]] to i32*<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[BC1]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])<br>
+  // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon* [[RES1]] to [4 x i8]*<br>
+  // CHECK: [[LOAD2:%[0-9]+]] = load [4 x i8], [4 x i8]* [[BC2]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
+<br>
+void test4() {<br>
+  struct T4A {<br>
+    union {<br>
+      struct {<br>
+        void *a;<br>
+      };<br>
+      struct {<br>
+        unsigned long b;<br>
+      };<br>
+    };<br>
+  };<br>
+<br>
+  struct T4A a = {<br>
+      .a = (void *)0x12345678,<br>
+  };<br>
+<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T4A, %struct.T4A* %a, i32 0, i32 0<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to %struct.anon*<br>
+  // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.anon, %struct.anon* [[BC1]], i32 0, i32 0<br>
+  // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES2]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]])<br>
+<br>
+  // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to %struct.anon.1*<br>
+  // CHECK: [[RES3:%[0-9]+]] = getelementptr inbounds %struct.anon.1, %struct.anon.1* [[BC2]], i32 0, i32 0<br>
+  // CHECK: [[LOAD2:%[0-9]+]] = load i64, i64* [[RES3]],<br>
+  // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD2]])<br>
+  // CHECK: call i32 (i8*, ...) @printf(<br>
+  __builtin_dump_struct(&a, &printf);<br>
+}<br>
<br>
Added: cfe/trunk/test/Sema/builtin-dump-struct.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/builtin-dump-struct.c?rev=329762&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/builtin-dump-struct.c?rev=329762&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Sema/builtin-dump-struct.c (added)<br>
+++ cfe/trunk/test/Sema/builtin-dump-struct.c Tue Apr 10 14:58:13 2018<br>
@@ -0,0 +1,42 @@<br>
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only -fno-spell-checking -verify %s<br>
+<br>
+void invalid_uses() {<br>
+  struct A {<br>
+  };<br>
+  struct A a;<br>
+  void *b;<br>
+  int (*goodfunc)(const char *, ...);<br>
+  int (*badfunc1)(const char *);<br>
+  int (*badfunc2)(int, ...);<br>
+  void (*badfunc3)(const char *, ...);<br>
+  int (*badfunc4)(char *, ...);<br>
+  int (*badfunc5)(void);<br>
+<br>
+  __builtin_dump_struct();             // expected-error {{too few arguments to function call, expected 2, have 0}}<br>
+  __builtin_dump_struct(1);            // expected-error {{too few arguments to function call, expected 2, have 1}}<br>
+  __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)}}<br>
+  __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 *, ...)')}}<br>
+  __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)}}<br>
+  __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 *, ...)')}}<br>
+  __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 *, ...)')}}<br>
+  __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 *, ...)')}}<br>
+  __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 *, ...)')}}<br>
+  __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 *, ...)')}}<br>
+  __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)}}<br>
+}<br>
+<br>
+void valid_uses() {<br>
+  struct A {<br>
+  };<br>
+  union B {<br>
+  };<br>
+<br>
+  int (*goodfunc)(const char *, ...);<br>
+  int (*goodfunc2)();<br>
+  struct A a;<br>
+  union B b;<br>
+<br>
+  __builtin_dump_struct(&a, goodfunc);<br>
+  __builtin_dump_struct(&b, goodfunc);<br>
+  __builtin_dump_struct(&a, goodfunc2);<br>
+}<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>