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