This is an archive of the discontinued LLVM Phabricator instance.

[BUILTINS] structure pretty printer
ClosedPublic

Authored by paulsemel on Mar 5 2018, 7:54 AM.

Details

Summary

The purpose of this new builtin is to be able to pretty print any structure at runtime.
This might be really useful when debugging is not an option or is fastidious (like for kernel development).

Diff Detail

Event Timeline

paulsemel created this revision.Mar 5 2018, 7:54 AM

Adding some reviewers.

One thing that's missing from this patch are test cases that demonstrate both the semantic checking (builtin accepts proper args, rejects improper ones, etc) and output.

lib/CodeGen/CGBuiltin.cpp
1204–1205

These declarations do not meet our coding style guidelines -- you should pick some new names (elsewhere as well).

1208

I don't see anything enforcing this constraint, so users are likely to hit this assertion rather than a compile error.

1211

Please don't use auto when the type is not explicitly spelled out in the initializer.

1218–1232

These should only be set up one time rather than each time someone calls the builtin.

1235–1236

Can be replaced with a range-based for loop over fields().

Also, the formatting of the braces doesn't match the coding standard (happens elsewhere as well) -- you should run your patch through clang-format: https://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting

1258

Are you intending to implement this as part of this functionality?

paulsemel marked 4 inline comments as done.Mar 6 2018, 2:04 AM
paulsemel added inline comments.
lib/CodeGen/CGBuiltin.cpp
1208

I actually didn't manage to enforce this in the builtin declaration as we can only declare simple types (I am probably missing something)

1258

Yes, my goal is to be able to dump the bitfields correctly, particularly if the structure is packed (for dumping a GDT for example).
I just didn't manage to do it properly for the moment.

paulsemel updated this revision to Diff 137141.Mar 6 2018, 2:40 AM

Applied Aaron change suggestions

aaron.ballman added inline comments.Mar 6 2018, 5:16 AM
lib/CodeGen/CGBuiltin.cpp
1206

You can use const auto * here because the type is spelled out in the initializer.

1208

I think this builtin will require custom type checking (so its signature needs a t), and then you can add your code to Sema::CheckBuiltinFunctionCall() to do the actual semantic checking.

1211

Please don't name the variable the same name as a type.

1231

It's unfortunate that you cannot distinguish between float and double. Do you need to use the format specifiers exactly?

What about other type information, like qualifiers or array extents? How should this handle types that do not exist in this mapping, like structure or enum types?

1238

const auto *

1258

Okay, this should probably be a FIXME comment if you don't intend to handle it in the initial patch. It should also have a test case with some comments about the test behavior.

paulsemel marked 2 inline comments as done.Mar 7 2018, 5:40 AM
paulsemel added inline comments.
lib/CodeGen/CGBuiltin.cpp
1206

After thinking about it, I find it clearer to spell the full type when possible (that's also probably because I'm used to code in C :) )

1231

So, I've think about it. What I am going to do is that if I do not know the type of the field, I am just going to print it as a pointer. I know that it is not the best solution, but I think it's a okay-ish solution until I implement the other types.
For the qualifiers, at it is printed the same way with and without those, I can just add the entries in the DenseMap.

paulsemel updated this revision to Diff 137369.Mar 7 2018, 5:48 AM

Applied Aaron suggestion changes.
Added parameters checking in Sema.

Please always upload patches with full context (-U999999)

paulsemel updated this revision to Diff 137371.Mar 7 2018, 5:58 AM

Updated with more context.

aaron.ballman added inline comments.Mar 7 2018, 8:29 AM
include/clang/Basic/DiagnosticSemaKinds.td
5022–5023 ↗(On Diff #137371)

Can you look to see if we have an existing diagnostic that can cover this instead?

lib/CodeGen/CGBuiltin.cpp
1218

This seems insufficient, as there are other qualifiers (restrict, ObjC GC qualifiers, etc). I think a better way to do this is to call QualType::getUnqualifiedType() on the type accessing the map.

1231

So, I've think about it. What I am going to do is that if I do not know the type of the field, I am just going to print it as a pointer. I know that it is not the best solution, but I think it's a okay-ish solution until I implement the other types.

Eek. That seems unfortunate. I'm thinking about very common use cases, like:

struct S {
  int i, j;
  float x, y;
};

struct T {
  struct S s;
  int k;
};

Printing out s as a pointer seems... not particularly useful.

1252

What about other types that have format specifiers (ptrdiff_t, size_t, intptr_t, char16_t, etc)?

1255

I don't think this comment adds a lot of value.

1269–1270

We generally prefer // style comments unless there's good reason to use /* */.

lib/Sema/SemaChecking.cpp
1114 ↗(On Diff #137371)

Comments should be grammatically correct, including punctuation (here and elsewhere).

1118 ↗(On Diff #137371)

You probably want to use IgnoreParenImpCasts().

1119 ↗(On Diff #137371)

Rather than calling is followed by get, you should just call get once and check the results here.

1121 ↗(On Diff #137371)

The string literals should be part of a %select in the diagnostic itself rather than printed this way.

1132–1133 ↗(On Diff #137371)

Same suggestions here.

Also, Arg1 and Arg0 aren't particularly descriptive names. Can you pick names based on the semantics of the arguments rather than their position?

1135 ↗(On Diff #137371)

What is a "printf like function pointer type"?

1155 ↗(On Diff #137371)

Spurious newline.

paulsemel marked 12 inline comments as done.Mar 8 2018, 8:15 AM
paulsemel added inline comments.Mar 8 2018, 8:15 AM
lib/CodeGen/CGBuiltin.cpp
1218

Yes, I think you're totally right !

1231

Yes, I see that this is true for other types that I am not handling for the moment.. What do you think is the best behavior for those cases ?
Just not print anything and go to the next entry ?

1252

So, for typedef, why not apply the QualType::getCanonicalType to our field type, so that we try to get rid of those intermediate typedefs ?

lib/Sema/SemaChecking.cpp
1121 ↗(On Diff #137371)

So, I am now using an other diagnostic definition, so I am constrained to keep using string literals I think..

1135 ↗(On Diff #137371)

I have changed this to the actual prototype of a "printf like" function so that it's way clearer of what I am expecting !

paulsemel updated this revision to Diff 137578.Mar 8 2018, 8:21 AM
paulsemel marked 3 inline comments as done.

Applied Aaron suggestions

aaron.ballman added inline comments.Mar 8 2018, 1:31 PM
lib/CodeGen/CGBuiltin.cpp
1231

I would probably try to recurse into the contained member with another layer of {} around its fields; if you restructured the code such that it is a function, this recursion probably wouldn't be too bad to implement.

1252

It should be possible to do for type aliases, because you know the canonical type information. However, that won't work for builtin types that aren't a typedef like char16_t.

paulsemel added inline comments.Mar 9 2018, 9:30 AM
lib/CodeGen/CGBuiltin.cpp
1252

Sure, but in this case, the only soluntion is to determine how we want to print those builtins and add those and the static map

paulsemel updated this revision to Diff 137775.Mar 9 2018, 9:32 AM

Added recursive type pretty-printing as suggested by Aaron.

Thanks for working on this! Few remarks in the comments.

lib/CodeGen/CGBuiltin.cpp
934

llvm::Value for consistency?

966

Indentation failed here.

976

I think you should use getelementptr instead of ptrtoint -> inttoptr https://llvm.org/docs/GetElementPtr.html

997

Indentation failed here too.

1003

If you use GEP you should be able to get rid of this cast here.

1009

You can probably use llvm::Twine for the concatenation of Format: http://llvm.org/doxygen/classllvm_1_1Twine.html.

lib/Sema/SemaChecking.cpp
1159 ↗(On Diff #137775)

if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FuncType))

paulsemel updated this revision to Diff 137998.Mar 12 2018, 7:00 AM
paulsemel marked 7 inline comments as done.

Applied Francis' suggestions

BTW, as far as i can tell this still has zero test coverage (no new tests are being added).
I'd expect to see the tests for the actual output

  • one struct per each type it is able to print
  • probably some tests showing error handling,

and possibly the availability of the builtin is somehow tested, too?

Hi,

BTW, as far as i can tell this still has zero test coverage (no new tests are being added).
I'd expect to see the tests for the actual output

  • one struct per each type it is able to print
  • probably some tests showing error handling,

and possibly the availability of the builtin is somehow tested, too?

Sure, I am going to work on it, now that the patch seems to be kind of "Okay" for its first version !

Thanks !

paulsemel updated this revision to Diff 138687.Mar 16 2018, 6:34 AM

Added some tests (unit tests for almost every types) and some other tests with tricky cases.

More tests are coming soon.

aaron.ballman added inline comments.Mar 16 2018, 11:51 AM
lib/CodeGen/CGBuiltin.cpp
935

Formatting looks off here and elsewhere. You should run the patch through clang-format. https://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting

938

Can use const auto * rather than spelling the type twice.

lib/Sema/SemaChecking.cpp
1128 ↗(On Diff #138687)

const auto *

1129 ↗(On Diff #138687)

I'd hoist this test to avoid duplicated code.

if (!PtrArgType->isPointerType() ||
    !PtrArgType->getPointeeType()->isRecordType())
1147–1148 ↗(On Diff #138687)

const auto *

1158 ↗(On Diff #138687)

const auto *

1159–1160 ↗(On Diff #138687)

You should also check that the first argument is const char * and add test cases for when it's not.

paulsemel updated this revision to Diff 138832.Mar 17 2018, 2:41 PM

Applied Aaron's suggestions
Added Sema tests for the typechecking

aaron.ballman added inline comments.Mar 20 2018, 8:59 AM
lib/Sema/SemaChecking.cpp
1122 ↗(On Diff #138832)

Drop all instances of this->.

1156 ↗(On Diff #138832)

Rather than building up a type only for comparison purposes, why not check:

QualType PT = FT->getParamType(0);
if (!PT->isPointerType() || !PT->getPointeeType()->isCharType() || !PT->getPointeeType().isConstQualified()) {
  Diag(...);
}
test/CodeGen/dump-struct-builtin.c
254 ↗(On Diff #138832)

I'd like to see some test cases using unions (perhaps mixing unions and inner struct types), tests using arrays, floating-point values, and type qualifiers on the data members.

test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Can you also add a test for: int (*badfunc4)(char *, ...); and int (*badfunc5)();

15 ↗(On Diff #138832)

Hrm, the 'structure pointer type' in this diagnostic is unfortunate because it's being quoted as though it were a real type -- you could drop the single quotes. If you think the resulting diagnostic reads too strangely, perhaps we will have to go back to a custom diagnostic after all.

17 ↗(On Diff #138832)

Why &goodfunc?

paulsemel marked 6 inline comments as done.Mar 29 2018, 11:47 AM
paulsemel added inline comments.
test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Isn't int (*func)() is a valid prototype for a printf like function in C ?
I instead added int (*func)(void) to the test cases.

15 ↗(On Diff #138832)

I think it will be better to just put something like structure pointer, so that we understand the type we are talking about.
But this diagnostic seems great, still, what do you think about sticking with it ?

17 ↗(On Diff #138832)

Yes, we already have a test like this anyway :)

paulsemel marked 3 inline comments as done.

Added Aaron suggestions.
Fixed a bug when having nested anonymous unions and structures.

Sorry for the delayed review; I'm back from vacation now and am picking things up again.

test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Isn't int (*func)() is a valid prototype for a printf like function in C ?

No, because it's missing the const char * as the mandatory first parameter. Do you want that to be allowed and hope the callee has it correct on their side, or do you want it to diagnose as not being a valid function?

15 ↗(On Diff #138832)

I think it's fine now; if it turns out to be horribly confusing to users, we can address it later.

17 ↗(On Diff #138832)

It was more a question of why the ampersand on the second argument -- I think that can be dropped and it'll be consistent with the rest of the tests.

paulsemel marked an inline comment as done.Apr 9 2018, 2:42 PM

No problem, thanks for getting back on this ! I was busy because of my midterms anyway :)

test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Actually, from a kernel developer perspective, I would say it's better to let the user do its stuff on his side, because kernel is full of trick !
But if you think I'd rather check whether we have int (*)(const char *, ...) at any time, we can go for it !

17 ↗(On Diff #138832)

Sure, sorry, I missed this. Totally agree with you, I am going to make the changes.

paulsemel updated this revision to Diff 141745.Apr 9 2018, 2:44 PM
paulsemel marked an inline comment as done.

Updated the amperstamp in the Sema test to be consistent with the remaining part of it.

aaron.ballman added inline comments.Apr 9 2018, 3:28 PM
test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Okay, if you think it'd be beneficial to allow a function without a prototype, I'm okay with it. Can you make it an explicit "good" test case?

paulsemel added inline comments.Apr 9 2018, 3:54 PM
test/Sema/builtin-dump-struct.c
8 ↗(On Diff #138832)

Sure :)

paulsemel updated this revision to Diff 141762.Apr 9 2018, 3:55 PM

Added a good test for the int (*)() function prototype, as we decided to accept it as a valid function for the dumper

aaron.ballman accepted this revision.Apr 10 2018, 7:57 AM

LGTM with some minor commenting nits.

lib/CodeGen/CGBuiltin.cpp
992

Missing full stop at the end of the comment. Same elsewhere.

lib/Sema/SemaChecking.cpp
1114 ↗(On Diff #141762)

Missing full stop here and elsewhere as well.

This revision is now accepted and ready to land.Apr 10 2018, 7:57 AM

I'm also often restricted to using printf for debugging so this looks really useful!

However, before committing this I feel like the test should also verify that the format strings that are generated are sensible.

Also what should happens when you have enum members in your struct or maybe even C++ pointers to members?

test/Sema/builtin-dump-struct.c
42 ↗(On Diff #141762)

I think there should also be a test here that we get an error when the struct contains bitfields instead of crashing/generating nonsense in CodeGen.

I'm also often restricted to using printf for debugging so this looks really useful!

However, before committing this I feel like the test should also verify that the format strings that are generated are sensible.

Also what should happens when you have enum members in your struct or maybe even C++ pointers to members?

So, for the moment, we are only handling basic types. That said, for the enum in C, we will print according to the type of the enum.
In the future versions, I really want to be able to print the name of the enum so that the output is more relevent.
Anyway, the rule I followed for the moment is : if I don't recognize the type, I print it as an address.

test/Sema/builtin-dump-struct.c
42 ↗(On Diff #141762)

Do you really think that I should throw an error just because there is a bitfield ?
I was thinking about just accepting the fact that the bitfield outputs are not correct but permit the user to pretty print the remaining part of the structure.
What do you think ?

arichardson accepted this revision.Apr 10 2018, 10:46 AM

So, for the moment, we are only handling basic types. That said, for the enum in C, we will print according to the type of the enum.
In the future versions, I really want to be able to print the name of the enum so that the output is more relevent.
Anyway, the rule I followed for the moment is : if I don't recognize the type, I print it as an address.

I you just print the address for unrecognized types then that should be fine. Thanks

test/Sema/builtin-dump-struct.c
42 ↗(On Diff #141762)

I would prefer an error instead of incorrect output since that can result in lots of unncessary debug work/false conclusions. But as long as it gets fixed/turned into an error before the final 7.0 release I think it shouldn't matter.

I don't really know what's the procedure right now.. What should I do once you both accepted the patch ? :)

I don't really know what's the procedure right now.. What should I do once you both accepted the patch ? :)

You're good to commit the patch and address the concerns raised by @arichardson in a follow-up patch. Do you need someone to commit on your behalf?

I don't really know what's the procedure right now.. What should I do once you both accepted the patch ? :)

You're good to commit the patch and address the concerns raised by @arichardson in a follow-up patch. Do you need someone to commit on your behalf?

Thanks! Yes please, I don't think I have the right accesses to do it myself

Can you rebase the patch? The patch did not apply cleanly for me against trunk (the contents of the check function are appearing in CheckARMBuiltinExclusiveCall()).

(Also, one tiny nit about escaped characters that can be fixed at the same time.)

lib/Sema/SemaChecking.cpp
1133 ↗(On Diff #141762)

Minor nit: can you remove the escape character for the single quotes (here and elsewhere in the patch), as there's no need to escape that character?

Patch rebased.
Minor fix for single quotes escaping.

Thank you for rebasing, but the patch does not pass tests locally for me. I get the following results (testing on Windows x64 with MSVC 2017 in a Debug build):

63>**** TEST 'Clang :: CodeGen/dump-struct-builtin.c' FAILED ****
63>Script:
63>--
63>d:\build\debug\bin\clang.EXE -cc1 -internal-isystem d:\build\debug\lib\clang\7.0.0\include -nostdsysteminc -emit-llvm D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c -o - | D:\build\Debug\bin\FileCheck.EXE D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c
63>--
63>Exit Code: 1
63>
63>Command Output (stdout):
63>--
63>$ "d:\build\debug\bin\clang.EXE" "-cc1" "-internal-isystem" "d:\build\debug\lib\clang\7.0.0\include" "-nostdsysteminc" "-emit-llvm" "D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c" "-o" "-"
63># command stderr:
63>D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c(20,30): warning G4AB12AC8: implicitly declaring library function 'printf' with type 'int (const char *, ...)'
63>
63> __builtin_dump_struct(&a, &printf);
63>
63> ^
63>
63>D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c:20:30: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
63>
63>1 warning generated.
63>
63>
63>$ "D:\build\Debug\bin\FileCheck.EXE" "D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c"
63># command stderr:
63>D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c(105,12): error G8E235623: expected string not found in input
63>
63> // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0
63>
63> ^
63>
63><stdin>:290:35: note: scanning from here
63>
63> %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @28, i32 0, i32 0))
63>
63> ^
63>
63><stdin>:291:2: note: possible intended match here
63>
63> %2 = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0
63>
63> ^
63>
63>
63>CUSTOMBUILD : error : command failed with exit status: 1
63>
63>--
63>
63>****

The output from the test on my machine is:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional>d:\build\debug\bin\clang.EXE -cc1 -internal-isystem d:\build\debug\lib\clang\7.0.0\include -nostdsysteminc -emit-llvm D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c -o -
D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c:20:30: warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)'

__builtin_dump_struct(&a, &printf);
                           ^

D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c:20:30: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
; ModuleID = 'D:\llvm\tools\clang\test\CodeGen\dump-struct-builtin.c'
source_filename = "D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"

%struct.U1A = type { i16 }
%struct.U2A = type { i16 }
%struct.U3A = type { i32 }
%struct.U4A = type { i32 }
%struct.U5A = type { i32 }
%struct.U6A = type { i32 }
%struct.U7A = type { i64 }
%struct.U8A = type { i64 }
%struct.U9A = type { i8 }
%struct.U10A = type { i8* }
%struct.U11A = type { i8* }
%struct.U12A = type { i8* }
%struct.U13A = type { i8* }
%struct.U14A = type { double }
%struct.U15A = type { [3 x i32] }
%struct.T1A = type { i32, i8* }
%struct.T2B = type { i32, %struct.T2A }
%struct.T2A = type { i32 }
%struct.T3A = type { %union.anon }
%union.anon = type { i32 }
%struct.T4A = type { %union.anon.0 }
%union.anon.0 = type { %struct.anon }
%struct.anon = type { i8* }
%struct.anon.1 = type { i32 }

$"??_C@_03OMINOMMP@LSE?$AA@" = comdat any

@unit1.a = private unnamed_addr constant %struct.U1A { i16 12 }, align 2
@0 = private unnamed_addr constant [14 x i8] c"struct U1A {\0A\00"
@1 = private unnamed_addr constant [11 x i8] c"short a : \00"
@2 = private unnamed_addr constant [5 x i8] c"%hd\0A\00"
@3 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit2.a = private unnamed_addr constant %struct.U2A { i16 12 }, align 2
@4 = private unnamed_addr constant [14 x i8] c"struct U2A {\0A\00"
@5 = private unnamed_addr constant [20 x i8] c"unsigned short a : \00"
@6 = private unnamed_addr constant [5 x i8] c"%hu\0A\00"
@7 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit3.a = private unnamed_addr constant %struct.U3A { i32 12 }, align 4
@8 = private unnamed_addr constant [14 x i8] c"struct U3A {\0A\00"
@9 = private unnamed_addr constant [9 x i8] c"int a : \00"
@10 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@11 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit4.a = private unnamed_addr constant %struct.U4A { i32 12 }, align 4
@12 = private unnamed_addr constant [14 x i8] c"struct U4A {\0A\00"
@13 = private unnamed_addr constant [18 x i8] c"unsigned int a : \00"
@14 = private unnamed_addr constant [4 x i8] c"%u\0A\00"
@15 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit5.a = private unnamed_addr constant %struct.U5A { i32 12 }, align 4
@16 = private unnamed_addr constant [14 x i8] c"struct U5A {\0A\00"
@17 = private unnamed_addr constant [10 x i8] c"long a : \00"
@18 = private unnamed_addr constant [5 x i8] c"%ld\0A\00"
@19 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit6.a = private unnamed_addr constant %struct.U6A { i32 12 }, align 4
@20 = private unnamed_addr constant [14 x i8] c"struct U6A {\0A\00"
@21 = private unnamed_addr constant [19 x i8] c"unsigned long a : \00"
@22 = private unnamed_addr constant [5 x i8] c"%lu\0A\00"
@23 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit7.a = private unnamed_addr constant %struct.U7A { i64 12 }, align 8
@24 = private unnamed_addr constant [14 x i8] c"struct U7A {\0A\00"
@25 = private unnamed_addr constant [15 x i8] c"long long a : \00"
@26 = private unnamed_addr constant [6 x i8] c"%lld\0A\00"
@27 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit8.a = private unnamed_addr constant %struct.U8A { i64 12 }, align 8
@28 = private unnamed_addr constant [14 x i8] c"struct U8A {\0A\00"
@29 = private unnamed_addr constant [15 x i8] c"long long a : \00"
@30 = private unnamed_addr constant [6 x i8] c"%lld\0A\00"
@31 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit9.a = private unnamed_addr constant %struct.U9A { i8 97 }, align 1
@32 = private unnamed_addr constant [14 x i8] c"struct U9A {\0A\00"
@33 = private unnamed_addr constant [10 x i8] c"char a : \00"
@34 = private unnamed_addr constant [4 x i8] c"%c\0A\00"
@35 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@"??_C@_03OMINOMMP@LSE?$AA@" = linkonce_odr dso_local unnamed_addr constant [4 x i8] c"LSE\00", comdat, align 1
@unit10.a = private unnamed_addr constant %struct.U10A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"??_C@_03OMINOMMP@LSE?$AA@", i32 0, i32 0) }, align 8
@36 = private unnamed_addr constant [15 x i8] c"struct U10A {\0A\00"
@37 = private unnamed_addr constant [12 x i8] c"char * a : \00"
@38 = private unnamed_addr constant [4 x i8] c"%s\0A\00"
@39 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit11.a = private unnamed_addr constant %struct.U11A { i8* inttoptr (i64 305419896 to i8*) }, align 8
@40 = private unnamed_addr constant [15 x i8] c"struct U11A {\0A\00"
@41 = private unnamed_addr constant [12 x i8] c"void * a : \00"
@42 = private unnamed_addr constant [4 x i8] c"%p\0A\00"
@43 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit12.a = private unnamed_addr constant %struct.U12A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"??_C@_03OMINOMMP@LSE?$AA@", i32 0, i32 0) }, align 8
@44 = private unnamed_addr constant [15 x i8] c"struct U12A {\0A\00"
@45 = private unnamed_addr constant [18 x i8] c"const char * a : \00"
@46 = private unnamed_addr constant [4 x i8] c"%p\0A\00"
@47 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit13.a = private unnamed_addr constant %struct.U13A { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"??_C@_03OMINOMMP@LSE?$AA@", i32 0, i32 0) }, align 8
@48 = private unnamed_addr constant [15 x i8] c"struct U13A {\0A\00"
@49 = private unnamed_addr constant [20 x i8] c"const charstar a : \00"
@50 = private unnamed_addr constant [4 x i8] c"%s\0A\00"
@51 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit14.a = private unnamed_addr constant %struct.U14A { double 0x3FF1F9ACFFA7EB6C }, align 8
@52 = private unnamed_addr constant [15 x i8] c"struct U14A {\0A\00"
@53 = private unnamed_addr constant [12 x i8] c"double a : \00"
@54 = private unnamed_addr constant [4 x i8] c"%f\0A\00"
@55 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@unit15.a = private unnamed_addr constant %struct.U15A { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
@56 = private unnamed_addr constant [15 x i8] c"struct U15A {\0A\00"
@57 = private unnamed_addr constant [13 x i8] c"int [3] a : \00"
@58 = private unnamed_addr constant [4 x i8] c"%p\0A\00"
@59 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@test1.a = private unnamed_addr constant %struct.T1A { i32 12, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"??_C@_03OMINOMMP@LSE?$AA@", i32 0, i32 0) }, align 8
@60 = private unnamed_addr constant [14 x i8] c"struct T1A {\0A\00"
@61 = private unnamed_addr constant [9 x i8] c"int a : \00"
@62 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@63 = private unnamed_addr constant [12 x i8] c"char * b : \00"
@64 = private unnamed_addr constant [4 x i8] c"%s\0A\00"
@65 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@test2.b = private unnamed_addr constant %struct.T2B { i32 24, %struct.T2A { i32 12 } }, align 4
@66 = private unnamed_addr constant [14 x i8] c"struct T2B {\0A\00"
@67 = private unnamed_addr constant [9 x i8] c"int b : \00"
@68 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@69 = private unnamed_addr constant [16 x i8] c"struct T2A a : \00"
@70 = private unnamed_addr constant [14 x i8] c"struct T2A {\0A\00"
@71 = private unnamed_addr constant [13 x i8] c" int a : \00"
@72 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@73 = private unnamed_addr constant [7 x i8] c" }\0A\00"
@74 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@test3.a = private unnamed_addr constant %struct.T3A { %union.anon { i32 42 } }, align 4
@75 = private unnamed_addr constant [14 x i8] c"struct T3A {\0A\00"
@76 = private unnamed_addr constant [91 x i8] c"union T3A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:334:5) : \00"
@77 = private unnamed_addr constant [90 x i8] c"union T3A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:334:5) {\0A\00"
@78 = private unnamed_addr constant [13 x i8] c" int a : \00"
@79 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@80 = private unnamed_addr constant [18 x i8] c" char [4] b : \00"
@81 = private unnamed_addr constant [4 x i8] c"%p\0A\00"
@82 = private unnamed_addr constant [7 x i8] c" }\0A\00"
@83 = private unnamed_addr constant [3 x i8] c"}\0A\00"
@test4.a = private unnamed_addr constant %struct.T4A { %union.anon.0 { %struct.anon { i8* inttoptr (i64 305419896 to i8*) } } }, align 8
@84 = private unnamed_addr constant [14 x i8] c"struct T4A {\0A\00"
@85 = private unnamed_addr constant [91 x i8] c"union T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:359:5) : \00"
@86 = private unnamed_addr constant [90 x i8] c"union T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:359:5) {\0A\00"
@87 = private unnamed_addr constant [96 x i8] c" struct T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:360:7) : \00"
@88 = private unnamed_addr constant [91 x i8] c"struct T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:360:7) {\0A\00"
@89 = private unnamed_addr constant [20 x i8] c" void * a : \00"
@90 = private unnamed_addr constant [4 x i8] c"%p\0A\00"
@91 = private unnamed_addr constant [11 x i8] c" }\0A\00"
@92 = private unnamed_addr constant [96 x i8] c" struct T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:363:7) : \00"
@93 = private unnamed_addr constant [91 x i8] c"struct T4A::(anonymous at D:\5Cllvm\5Ctools\5Cclang\5Ctest\5CCodeGen\5Cdump-struct-builtin.c:363:7) {\0A\00"
@94 = private unnamed_addr constant [27 x i8] c" unsigned long b : \00"
@95 = private unnamed_addr constant [5 x i8] c"%lu\0A\00"
@96 = private unnamed_addr constant [11 x i8] c" }\0A\00"
@97 = private unnamed_addr constant [7 x i8] c" }\0A\00"
@98 = private unnamed_addr constant [3 x i8] c"}\0A\00"

; Function Attrs: noinline nounwind optnone
define dso_local void @unit1() #0 {
entry:

%a = alloca %struct.U1A, align 2
%0 = bitcast %struct.U1A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 2 %0, i8* align 2 bitcast (%struct.U1A* @unit1.a to i8*), i64 2, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @0, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @1, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i16, i16* %2, align 2
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @2, i32 0, i32 0), i16 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @3, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) #1

declare dso_local i32 @printf(i8*, ...) #2

; Function Attrs: noinline nounwind optnone
define dso_local void @unit2() #0 {
entry:

%a = alloca %struct.U2A, align 2
%0 = bitcast %struct.U2A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 2 %0, i8* align 2 bitcast (%struct.U2A* @unit2.a to i8*), i64 2, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @4, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @5, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i16, i16* %2, align 2
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @6, i32 0, i32 0), i16 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @7, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit3() #0 {
entry:

%a = alloca %struct.U3A, align 4
%0 = bitcast %struct.U3A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U3A* @unit3.a to i8*), i64 4, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @8, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @9, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @10, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @11, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit4() #0 {
entry:

%a = alloca %struct.U4A, align 4
%0 = bitcast %struct.U4A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U4A* @unit4.a to i8*), i64 4, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @12, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @13, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @14, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @15, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit5() #0 {
entry:

%a = alloca %struct.U5A, align 4
%0 = bitcast %struct.U5A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U5A* @unit5.a to i8*), i64 4, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @16, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @17, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @18, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @19, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit6() #0 {
entry:

%a = alloca %struct.U6A, align 4
%0 = bitcast %struct.U6A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U6A* @unit6.a to i8*), i64 4, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @20, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @21, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @22, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @23, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit7() #0 {
entry:

%a = alloca %struct.U7A, align 8
%0 = bitcast %struct.U7A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U7A* @unit7.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @24, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @25, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i64, i64* %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @26, i32 0, i32 0), i64 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @27, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit8() #0 {
entry:

%a = alloca %struct.U8A, align 8
%0 = bitcast %struct.U8A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U8A* @unit8.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @28, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @29, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i64, i64* %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @30, i32 0, i32 0), i64 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @31, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit9() #0 {
entry:

%a = alloca %struct.U9A, align 1
%0 = bitcast %struct.U9A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 getelementptr inbounds (%struct.U9A, %struct.U9A* @unit9.a, i32 0, i32 0), i64 1, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @32, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @33, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i8, i8* %2, align 1
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @34, i32 0, i32 0), i8 %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @35, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit10() #0 {
entry:

%a = alloca %struct.U10A, align 8
%0 = bitcast %struct.U10A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U10A* @unit10.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @36, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @37, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i8*, i8** %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @38, i32 0, i32 0), i8* %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @39, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit11() #0 {
entry:

%a = alloca %struct.U11A, align 8
%0 = bitcast %struct.U11A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U11A* @unit11.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @40, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @41, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i8*, i8** %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @42, i32 0, i32 0), i8* %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @43, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit12() #0 {
entry:

%a = alloca %struct.U12A, align 8
%0 = bitcast %struct.U12A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U12A* @unit12.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @44, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U12A, %struct.U12A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @45, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i8*, i8** %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @46, i32 0, i32 0), i8* %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @47, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit13() #0 {
entry:

%a = alloca %struct.U13A, align 8
%0 = bitcast %struct.U13A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U13A* @unit13.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @48, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U13A, %struct.U13A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @49, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i8*, i8** %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @50, i32 0, i32 0), i8* %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @51, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit14() #0 {
entry:

%a = alloca %struct.U14A, align 8
%0 = bitcast %struct.U14A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.U14A* @unit14.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @52, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U14A, %struct.U14A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @53, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load double, double* %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @54, i32 0, i32 0), double %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @55, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @unit15() #0 {
entry:

%a = alloca %struct.U15A, align 4
%0 = bitcast %struct.U15A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U15A* @unit15.a to i8*), i64 12, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @56, i32 0, i32 0))
%2 = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @57, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load [3 x i32], [3 x i32]* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @58, i32 0, i32 0), [3 x i32] %5)
%7 = add i32 %4, %6
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @59, i32 0, i32 0))
%9 = add i32 %7, %8
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @test1() #0 {
entry:

%a = alloca %struct.T1A, align 8
%0 = bitcast %struct.T1A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.T1A* @test1.a to i8*), i64 16, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @60, i32 0, i32 0))
%2 = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @61, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @62, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 1
%9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @63, i32 0, i32 0))
%10 = add i32 %7, %9
%11 = load i8*, i8** %8, align 8
%12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @64, i32 0, i32 0), i8* %11)
%13 = add i32 %10, %12
%14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @65, i32 0, i32 0))
%15 = add i32 %13, %14
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @test2() #0 {
entry:

%b = alloca %struct.T2B, align 4
%0 = bitcast %struct.T2B* %b to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.T2B* @test2.b to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @66, i32 0, i32 0))
%2 = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @67, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = load i32, i32* %2, align 4
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @68, i32 0, i32 0), i32 %5)
%7 = add i32 %4, %6
%8 = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 1
%9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @69, i32 0, i32 0))
%10 = add i32 %7, %9
%11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @70, i32 0, i32 0))
%12 = getelementptr inbounds %struct.T2A, %struct.T2A* %8, i32 0, i32 0
%13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @71, i32 0, i32 0))
%14 = add i32 %11, %13
%15 = load i32, i32* %12, align 4
%16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @72, i32 0, i32 0), i32 %15)
%17 = add i32 %14, %16
%18 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @73, i32 0, i32 0))
%19 = add i32 %17, %18
%20 = add i32 %19, %10
%21 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @74, i32 0, i32 0))
%22 = add i32 %20, %21
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @test3() #0 {
entry:

%a = alloca %struct.T3A, align 4
%0 = bitcast %struct.T3A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.T3A* @test3.a to i8*), i64 4, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @75, i32 0, i32 0))
%2 = getelementptr inbounds %struct.T3A, %struct.T3A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([91 x i8], [91 x i8]* @76, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([90 x i8], [90 x i8]* @77, i32 0, i32 0))
%6 = bitcast %union.anon* %2 to i32*
%7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @78, i32 0, i32 0))
%8 = add i32 %5, %7
%9 = load i32, i32* %6, align 4
%10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @79, i32 0, i32 0), i32 %9)
%11 = add i32 %8, %10
%12 = bitcast %union.anon* %2 to [4 x i8]*
%13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @80, i32 0, i32 0))
%14 = add i32 %11, %13
%15 = load [4 x i8], [4 x i8]* %12, align 4
%16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @81, i32 0, i32 0), [4 x i8] %15)
%17 = add i32 %14, %16
%18 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @82, i32 0, i32 0))
%19 = add i32 %17, %18
%20 = add i32 %19, %4
%21 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @83, i32 0, i32 0))
%22 = add i32 %20, %21
ret void

}

; Function Attrs: noinline nounwind optnone
define dso_local void @test4() #0 {
entry:

%a = alloca %struct.T4A, align 8
%0 = bitcast %struct.T4A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 bitcast (%struct.T4A* @test4.a to i8*), i64 8, i1 false)
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @84, i32 0, i32 0))
%2 = getelementptr inbounds %struct.T4A, %struct.T4A* %a, i32 0, i32 0
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([91 x i8], [91 x i8]* @85, i32 0, i32 0))
%4 = add i32 %1, %3
%5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([90 x i8], [90 x i8]* @86, i32 0, i32 0))
%6 = bitcast %union.anon.0* %2 to %struct.anon*
%7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([96 x i8], [96 x i8]* @87, i32 0, i32 0))
%8 = add i32 %5, %7
%9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([91 x i8], [91 x i8]* @88, i32 0, i32 0))
%10 = getelementptr inbounds %struct.anon, %struct.anon* %6, i32 0, i32 0
%11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @89, i32 0, i32 0))
%12 = add i32 %9, %11
%13 = load i8*, i8** %10, align 8
%14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @90, i32 0, i32 0), i8* %13)
%15 = add i32 %12, %14
%16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @91, i32 0, i32 0))
%17 = add i32 %15, %16
%18 = add i32 %17, %8
%19 = bitcast %union.anon.0* %2 to %struct.anon.1*
%20 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([96 x i8], [96 x i8]* @92, i32 0, i32 0))
%21 = add i32 %18, %20
%22 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([91 x i8], [91 x i8]* @93, i32 0, i32 0))
%23 = getelementptr inbounds %struct.anon.1, %struct.anon.1* %19, i32 0, i32 0
%24 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @94, i32 0, i32 0))
%25 = add i32 %22, %24
%26 = load i32, i32* %23, align 8
%27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @95, i32 0, i32 0), i32 %26)
%28 = add i32 %25, %27
%29 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @96, i32 0, i32 0))
%30 = add i32 %28, %29
%31 = add i32 %30, %21
%32 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @97, i32 0, i32 0))
%33 = add i32 %31, %32
%34 = add i32 %33, %4
%35 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @98, i32 0, i32 0))
%36 = add i32 %34, %35
ret void

}

attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { argmemonly nounwind }
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 2}
!1 = !{!"clang version 7.0.0 (trunk 329536)"}
1 warning generated.

Sorry about it, I also have the warning on my machine, but not the error you get...
Those test are actually working on my different linux machines, that's really weird.
This one is actually really weird, because I could find manually the missing pattern in your output.. I just don't get what is happening.

paulsemel updated this revision to Diff 141913.Apr 10 2018, 2:23 PM

Fixed printf warning generated in tests/CodeGen.

Ok, I found the problem. In fact the size of long is 4 bytes on your machine, but 8 bytes on mine.
This makes this // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], fail.
Do you know a smart way to do it without dealing with type sizes ?

lebedev.ri added inline comments.Apr 10 2018, 2:48 PM
test/CodeGen/dump-struct-builtin.c
1 ↗(On Diff #141913)

This should be

// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -emit-llvm %s -o - | FileCheck %s

or something

Ok, I found the problem. In fact the size of long is 4 bytes on your machine, but 8 bytes on mine.
This makes this // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], fail.
Do you know a smart way to do it without dealing with type sizes ?

You should probably run the test with -triple=x86_64-unknown-linux (or whatever the triple for your host machine happens to be). Otherwise they depend on the default clang triple which could have a different size long.

Ok, I found the problem. In fact the size of long is 4 bytes on your machine, but 8 bytes on mine.
This makes this // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], fail.
Do you know a smart way to do it without dealing with type sizes ?

You should probably run the test with -triple=x86_64-unknown-linux (or whatever the triple for your host machine happens to be). Otherwise they depend on the default clang triple which could have a different size long.

Thanks a lot for your help, updating the patch !

Sorry about it, I also have the warning on my machine, but not the error you get...
Those test are actually working on my different linux machines, that's really weird.
This one is actually really weird, because I could find manually the missing pattern in your output.. I just don't get what is happening.

I think the issue has to do with bit-width. From unit5():

// Your test

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

// My results

; Function Attrs: noinline nounwind optnone
define dso_local void @unit5() #0 {
entry:

  %a = alloca %struct.U5A, align 4
  %0 = bitcast %struct.U5A* %a to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.U5A* @unit5.a to i8*), i64 4, i1 false)
  %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @16, i32 0, i32 0))
  %2 = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0
  %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @17, i32 0, i32 0))
  %4 = add i32 %1, %3
  %5 = load i32, i32* %2, align 4
  %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @18, i32 0, i32 0), i32 %5)
  %7 = add i32 %4, %6
  %8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @19, i32 0, i32 0))
  %9 = add i32 %7, %8
  ret void
}

You are testing that the load is an i64 but in my results it's an i32. An explicit target triple on the RUN line solves the issue.

paulsemel updated this revision to Diff 141920.Apr 10 2018, 3:00 PM

Added triple option to CodeGen test so that it matches with the correct architecture

aaron.ballman closed this revision.Apr 10 2018, 3:01 PM

I see we all found the solution at the same time. Yay teamwork! :-D

I've committed (with the test fixed up) in r329762.