Created attachment 32208 [details] va_list corruption simple example The code in attachment (for convenience see here http://coliru.stacked-crooked.com/a/14319753d1667629) prints garbage instead of 6-th argument if Dummy struct is empty. Adding some member to this struct "fixes" the problem. Not sure, whether problem is in Gcc code generation or libc, but probably related to ABI. Tested on Ubuntu 13.04 64bit.
Created attachment 32212 [details] pre-processed test case, valid C and C++ The following is a pre-processed and simplified test case, tweaked to compile as both C and C++. As C it succeeds, as C++ it fails. Tested with gcc/g++ 4.8.2 on x86_64-linux.
Should g++ put pass the empty struct on stack?
(In reply to H.J. Lu from comment #2) > Should g++ put pass the empty struct on stack? It is a target bug if it is passing on the stack. Note in C++, the size of the struct is 1 while in C, the size is 0.
(In reply to Andrew Pinski from comment #3) > (In reply to H.J. Lu from comment #2) > > Should g++ put pass the empty struct on stack? > > It is a target bug if it is passing on the stack. Note in C++, the size of > the struct is 1 while in C, the size is 0. Can someone try this on non-x86 targets? [hjl@gnu-6 pr60336]$ cat x.ii struct dummy { }; struct foo { int i1; int i2; int i3; int i4; int i5; }; extern "C" void fun(struct dummy, struct foo); int main() { struct dummy d; struct foo f = { -1, -2, -3, -4, -5 }; fun(d, f); return 0; } [hjl@gnu-6 pr60336]$ cat fun.i struct dummy { }; struct foo { int i1; int i2; int i3; int i4; int i5; }; void fun(struct dummy d, struct foo f) { if (f.i1 != -1) __builtin_abort(); } [hjl@gnu-6 pr60336]$ gcc -c fun.i [hjl@gnu-6 pr60336]$ gcc -c x.ii [hjl@gnu-6 pr60336]$ g++ fun.o x.o [hjl@gnu-6 pr60336]$ ./a.out Aborted [hjl@gnu-6 pr60336]$ Is this test valid? BTW, clang works fine on x86.
(In reply to Andrew Pinski from comment #3) > It is a target bug if it is passing on the stack. Note in C++, the size of > the struct is 1 while in C, the size is 0. Changing the testcase a bit: fun(d, 2, 3, 4, 5, 6, 7, 8); cc1plus -O2 -maccumulate-outgoing-args: xorl %eax, %eax movl $7, %r9d movl $8, 8(%rsp) movb $0, (%rsp) <--- this shouldn't be there. movl $6, %r8d movl $5, %ecx movl $4, %edx movl $3, %esi movl $2, %edi call _Z3fun5dummyiz clang -O2: movl $8, (%rsp) movl $3, %esi movl $4, %edx movl $5, %ecx movl $6, %r8d movl $7, %r9d xorl %eax, %eax callq _Z3fun5dummyiz
(In reply to H.J. Lu from comment #4) > Can someone try this on non-x86 targets? I get abort on alpha: $ gcc -c fun.i $ gcc -c x.ii $ g++ fun.o x.o $ ./a.out Aborted
(In reply to H.J. Lu from comment #4) > Is this test valid? BTW, clang works fine on x86. No this testcase is not valid at all. See http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Empty-Structures.html#Empty-Structures where it is documented it is not valid.
This works: diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 00773d8..16669b9 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7193,6 +7193,7 @@ function_arg_advance_64 (CUMULATIVE_ARGS *cum, enum machine_mode mode, return; if (examine_argument (mode, type, 0, &int_nregs, &sse_nregs) + && (sse_nregs || int_nregs) && sse_nregs <= cum->sse_nregs && int_nregs <= cum->nregs) { cum->nregs -= int_nregs;
(In reply to Andrew Pinski from comment #7) > (In reply to H.J. Lu from comment #4) > > Is this test valid? BTW, clang works fine on x86. > > No this testcase is not valid at all. See > http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Empty-Structures.html#Empty- > Structures where it is documented it is not valid. How it proves that the test case is invalid? Independently on what size has empty structure the called function absolutely legally can expect it receives the values which were passed by the caller. A compiler should care it works for both C and C++. Am I wrong? Also as I remember in C++ size of empty structure is sizeof(char) except the case some class is derived from the empty structure. In this case it adds no overhead to the size of the derived class itself (thus virtually has zero size in this case).
(In reply to vagran from comment #9) > (In reply to Andrew Pinski from comment #7) > > (In reply to H.J. Lu from comment #4) > > > Is this test valid? BTW, clang works fine on x86. > > > > No this testcase is not valid at all. See > > http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Empty-Structures.html#Empty- > > Structures where it is documented it is not valid. > > How it proves that the test case is invalid? I am not saying the original testcase (variable argument case) is invalid, just the testcase where you are passing an empty struct between C and C++ one is invalid.
This patch may be better: diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 00773d8..426146a 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -6842,7 +6842,7 @@ examine_argument (enum machine_mode mode, const_tree type, int in_return, case X86_64_MEMORY_CLASS: gcc_unreachable (); } - return 1; + return *int_nregs || *sse_nregs; } /* Construct container for the argument used by GCC interface. See
It is a bit alarming that gcc, clang and clang++ use one ABI and g++ uses a different (inferior) one (the incompatibility with clang++ should affect some standard library functions, though they are often inlined). Also, extern "C" is a calling convention marker, and it is rather disrespectful of g++ to ignore it, even if this is only about a non-standard (but documented) extension.
Passing struct dummy { }; is still odd for g++. It is supposed to have a single member of type char, which should be passed in register, not on stack.
(In reply to H.J. Lu from comment #13) > Passing > > struct dummy { }; > > is still odd for g++. It is supposed to have a single member of type char, > which should be passed in register, not on stack. This passes it in register: diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 00773d8..6975ff1 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -6613,6 +6613,8 @@ classify_argument (enum machine_mode mode, const_tree type, return 0; } } + if (mode == QImode && words == 1 && classes[0] == X86_64_NO_CLASS) + classes[0] = X86_64_INTEGERSI_CLASS; return words; }
(In reply to Andrew Pinski from comment #7) > No this testcase is not valid at all. See > http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Empty-Structures.html#Empty- > Structures where it is documented it is not valid. That only documents that sizeof is different for C and C++, the calling convention should be the same. And it seems like classify_argument should already be returning 1 with classes[0]==NO_CLASS for both C and C++. Why are we getting different results?
(In reply to Jason Merrill from comment #15) > (In reply to Andrew Pinski from comment #7) > > No this testcase is not valid at all. See > > http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Empty-Structures.html#Empty- > > Structures where it is documented it is not valid. > > That only documents that sizeof is different for C and C++, the calling > convention should be the same. And it seems like classify_argument should > already be returning 1 with classes[0]==NO_CLASS for both C and C++. Why > are we getting different results? X86 backend has some issues. But the C/C++ calling convention issue affects most, if not all, targets. You can try the test case in comment 4 on non-x86 targets.
(In reply to Jason Merrill from comment #15) > That only documents that sizeof is different for C and C++, the calling > convention should be the same. And it seems like classify_argument should > already be returning 1 with classes[0]==NO_CLASS for both C and C++. Why > are we getting different results? classify_argument has an early exit: /* Zero sized arrays or structures are NO_CLASS. We return 0 to signalize memory class, so handle it as special case. */ if (!words) { classes[0] = X86_64_NO_CLASS; return 1; } but it doesn't trigger for c++, where words gets calculated as 1.
(In reply to Uroš Bizjak from comment #17) > classify_argument has an early exit: > > /* Zero sized arrays or structures are NO_CLASS. We return 0 to > signalize memory class, so handle it as special case. */ > > if (!words) > { > classes[0] = X86_64_NO_CLASS; > return 1; > } > > but it doesn't trigger for c++, where words gets calculated as 1. Yes, it looks like this special case is in order to return the same thing that C++ does through the normal logic: normally we return 'words', and since there are no fields classes[0] is still initialized to NO_CLASS.
Created attachment 32228 [details] HJ's test in dejagnu form Here's HJ's testcase in a form that can be dropped into g++.dg/abi.
(In reply to H.J. Lu from comment #4) > Can someone try this on non-x86 targets? gcc110 (ppc64) from the compile farm: [uros@gcc1-power7 ~]$ gcc -c fun.i [uros@gcc1-power7 ~]$ gcc -c x.ii [uros@gcc1-power7 ~]$ g++ fun.o x.o [uros@gcc1-power7 ~]$ ./a.out Aborted I don't think this is a target bug.
(In reply to Marc Glisse from comment #12) > It is a bit alarming that gcc, clang and clang++ use one ABI and g++ uses a > different (inferior) one (the incompatibility with clang++ should affect > some standard library functions, though they are often inlined). Also, > extern "C" is a calling convention marker, and it is rather disrespectful of > g++ to ignore it, even if this is only about a non-standard (but documented) > extension. See http://gcc.gnu.org/ml/gcc-patches/2006-05/msg00868.html which was about adding a warning for the extern "C" case. And http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10208 for the ABI difference in the past about C/C++. There is most likely more discussion about this in other threads too.
This is a target independent issue. Clang++ skips empty struct argument and g++ passes it. Skip empty struct argument requires middle-end changes.
(In reply to H.J. Lu from comment #22) > This is a target independent issue. Clang++ skips empty struct argument > and g++ passes it. Skip empty struct argument requires middle-end changes. Except in c++, the struct is non zero in size.
Just to be on a safe side, please, also do not forget that empty struct (or class) is really zero in the case when another structure (or class) is derived from it. For example, such test would be useful after fix: struct A {}; struct B: A { int i; }; assert(sizeof(B) == sizeof(int)); And something like this: struct A {}; struct B: A {}; struct C: B {}; struct D: C {}; assert(sizeof(D) == 1);
(In reply to Andrew Pinski from comment #23) > (In reply to H.J. Lu from comment #22) > > This is a target independent issue. Clang++ skips empty struct argument > > and g++ passes it. Skip empty struct argument requires middle-end changes. > > Except in c++, the struct is non zero in size. I think empty struct should be considered as zero size for argument passing, like clang does. This bug causes PR 67239.
Created attachment 36720 [details] A patch I am testing this patch.
Comment on attachment 36720 [details] A patch How does this interact with LTO where lang_hooks.decls.empty_record_p is not defined?
Created attachment 36721 [details] A patch An updated patch with middle end changes.
(In reply to Andrew Pinski from comment #27) > Comment on attachment 36720 [details] > A patch > > How does this interact with LTO where lang_hooks.decls.empty_record_p is not > defined? We can stream out empty record info and use it to in lto_empty_record_p.
(In reply to H.J. Lu from comment #29) > (In reply to Andrew Pinski from comment #27) > > Comment on attachment 36720 [details] > > A patch > > > > How does this interact with LTO where lang_hooks.decls.empty_record_p is not > > defined? > > We can stream out empty record info and use it to in lto_empty_record_p. Isn't an empty record is one without any field decls?
(In reply to Andrew Pinski from comment #30) > > Isn't an empty record is one without any field decls? I thin it is language specific.
(In reply to H.J. Lu from comment #31) > (In reply to Andrew Pinski from comment #30) > > > > Isn't an empty record is one without any field decls? > > I thin it is language specific. How so? An record without any field decls is empty and most of the rest of the middle-end treats it that way.
(In reply to Andrew Pinski from comment #32) > (In reply to H.J. Lu from comment #31) > > (In reply to Andrew Pinski from comment #30) > > > > > > Isn't an empty record is one without any field decls? > > > > I thin it is language specific. > > How so? An record without any field decls is empty and most of the rest of > the middle-end treats it that way. This is a C++ empty class with field decls from PR 68355: (gdb) cal debug_tree (type) <record_type 0x7ffff164e540 integral_constant type_5 type_6 QI size <integer_cst 0x7ffff14e5f90 type <integer_type 0x7ffff14e92a0 bitsizetype> constant 8> unit size <integer_cst 0x7ffff14e5fa8 type <integer_type 0x7ffff14e91f8 sizetype> constant 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff164e540 fields <var_decl 0x7ffff7ff9d80 value type <boolean_type 0x7ffff164e9d8 bool readonly unsigned type_6 QI size <integer_cst 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff164e9d8 precision 1 min <integer_cst 0x7ffff1506210 0> max <integer_cst 0x7ffff1506240 1>> readonly constant used public static tree_3 unsigned external nonlocal decl_3 decl_6 QI file bar.ii line 4 col 24 size <integer_cst 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> align 8 context <record_type 0x7ffff164e540 integral_constant> template-info 0x7ffff164c220 chain <type_decl 0x7ffff1645850 integral_constant type <record_type 0x7ffff164e930 integral_constant> external nonlocal suppress-debug decl_4 VOID file bar.ii line 3 col 1 align 8 context <record_type 0x7ffff164e540 integral_constant> result <record_type 0x7ffff164e540 integral_constant> chain <type_decl 0x7ffff16458e8 value_type>>> context <translation_unit_decl 0x7ffff14f1168 D.1> full-name "struct integral_constant<bool, true>" X() has-type-conversion X(constX&) this=(X&) n_parents=0 use_template=1 interface-unknown pointer_to_this <pointer_type 0x7ffff1650000> reference_to_this <reference_type 0x7ffff1650498> chain <type_decl 0x7ffff1645720 integral_constant>> (gdb)
(In reply to H.J. Lu from comment #33) > (In reply to Andrew Pinski from comment #32) > > (In reply to H.J. Lu from comment #31) > > > (In reply to Andrew Pinski from comment #30) > > > > > > > > Isn't an empty record is one without any field decls? > > > > > > I thin it is language specific. > > > > How so? An record without any field decls is empty and most of the rest of > > the middle-end treats it that way. > > This is a C++ empty class with field decls from PR 68355: > > (gdb) cal debug_tree (type) > <record_type 0x7ffff164e540 integral_constant type_5 type_6 QI > size <integer_cst 0x7ffff14e5f90 type <integer_type 0x7ffff14e92a0 > bitsizetype> constant 8> > unit size <integer_cst 0x7ffff14e5fa8 type <integer_type 0x7ffff14e91f8 > sizetype> constant 1> > align 8 symtab 0 alias set -1 canonical type 0x7ffff164e540 > fields <var_decl 0x7ffff7ff9d80 value > type <boolean_type 0x7ffff164e9d8 bool readonly unsigned type_6 QI > size <integer_cst 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> > align 8 symtab 0 alias set -1 canonical type 0x7ffff164e9d8 > precision 1 min <integer_cst 0x7ffff1506210 0> max <integer_cst > 0x7ffff1506240 1>> > readonly constant used public static tree_3 unsigned external > nonlocal decl_3 decl_6 QI file bar.ii line 4 col 24 size <integer_cst > 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> > align 8 context <record_type 0x7ffff164e540 integral_constant> > template-info 0x7ffff164c220 > chain <type_decl 0x7ffff1645850 integral_constant type <record_type > 0x7ffff164e930 integral_constant> > external nonlocal suppress-debug decl_4 VOID file bar.ii line 3 > col 1 > align 8 context <record_type 0x7ffff164e540 integral_constant> > result <record_type 0x7ffff164e540 integral_constant> > chain <type_decl 0x7ffff16458e8 value_type>>> context > <translation_unit_decl 0x7ffff14f1168 D.1> > full-name "struct integral_constant<bool, true>" > X() has-type-conversion X(constX&) this=(X&) n_parents=0 use_template=1 > interface-unknown > pointer_to_this <pointer_type 0x7ffff1650000> reference_to_this > <reference_type 0x7ffff1650498> chain <type_decl 0x7ffff1645720 > integral_constant>> > (gdb) So no FIELD_DECL as part of fields; only var or type or method_decl?
(In reply to Andrew Pinski from comment #34) > > So no FIELD_DECL as part of fields; only var or type or method_decl? struct dummy { }; struct true_type { struct dummy i; }; gave: (gdb) cal debug_tree (type) <record_type 0x7ffff1647dc8 true_type type_5 type_6 QI size <integer_cst 0x7ffff14e5f90 type <integer_type 0x7ffff14e92a0 bitsizetype> constant 8> unit size <integer_cst 0x7ffff14e5fa8 type <integer_type 0x7ffff14e91f8 sizetype> constant 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff1647dc8 fields <field_decl 0x7ffff1645720 i type <record_type 0x7ffff1647bd0 dummy type_5 type_6 QI size <integer_cst 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> align 8 symtab 0 alias set -1 canonical type 0x7ffff1647bd0 fields <type_decl 0x7ffff1645428 dummy> context <translation_unit_decl 0x7ffff14f1168 D.1> full-name "struct dummy" X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown pointer_to_this <pointer_type 0x7ffff164b348> reference_to_this <reference_type 0x7ffff164b1f8> chain <type_decl 0x7ffff1645390 dummy>> nonlocal decl_3 QI file z.ii line 2 col 33 size <integer_cst 0x7ffff14e5f90 8> unit size <integer_cst 0x7ffff14e5fa8 1> align 8 offset_align 128 offset <integer_cst 0x7ffff14e5e88 constant 0> bit offset <integer_cst 0x7ffff14e5ee8 constant 0> context <record_type 0x7ffff1647dc8 true_type> chain <type_decl 0x7ffff1645688 true_type type <record_type 0x7ffff1647e70 true_type> nonlocal decl_4 VOID file z.ii line 2 col 18 align 1 context <record_type 0x7ffff1647dc8 true_type> result <record_type 0x7ffff1647dc8 true_type> >> context <translation_unit_decl 0x7ffff14f1168 D.1> full-name "struct true_type" X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown pointer_to_this <pointer_type 0x7ffff164b150> reference_to_this <reference_type 0x7ffff164f1f8> chain <type_decl 0x7ffff16455f0 true_type>> (gdb)
Created attachment 36723 [details] An updated patch to use is_really_empty_class I am testing this.
(In reply to H.J. Lu from comment #35) > (In reply to Andrew Pinski from comment #34) > > > > So no FIELD_DECL as part of fields; only var or type or method_decl? > > struct dummy { }; > struct true_type { struct dummy i; }; This is two empty structs. The same is true even in GNU C. So a struct which contains no field decls or field decls which is an empty struct is also an empty struct.
Created attachment 36724 [details] An updated patch to add empty_record_p I am testing it now.
You should also update gimplify.c's zero_sized_type to be the same as your empty_record_p.
(In reply to H.J. Lu from comment #38) > Created attachment 36724 [details] > An updated patch to add empty_record_p > > I am testing it now. It doesn't work since it misses for (binfo = TYPE_BINFO (type), i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) if (!is_really_empty_class (BINFO_TYPE (base_binfo))) return false; in is_really_empty_class in cp/class.c. It is language specific.
(In reply to Andrew Pinski from comment #39) > You should also update gimplify.c's zero_sized_type to be the same as your > empty_record_p. It won't work since the size of empty class isn't zero in C++. We just pass and return it like zero size record in C++.
(In reply to H.J. Lu from comment #29) > (In reply to Andrew Pinski from comment #27) > > Comment on attachment 36720 [details] > > A patch > > > > How does this interact with LTO where lang_hooks.decls.empty_record_p is not > > defined? > > We can stream out empty record info and use it to in lto_empty_record_p. There are 7 bits we can use for LTO: /* These flags are available for each language front end to use internally. */ #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_0) #define TYPE_LANG_FLAG_1(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_1) #define TYPE_LANG_FLAG_2(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_2) #define TYPE_LANG_FLAG_3(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_3) #define TYPE_LANG_FLAG_4(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_4) #define TYPE_LANG_FLAG_5(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_5) #define TYPE_LANG_FLAG_6(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_6)
Created attachment 36730 [details] A patch to support LTO I am testing this patch.
*** Bug 68355 has been marked as a duplicate of this bug. ***
I opened a clang bug: https://llvm.org/bugs/show_bug.cgi?id=26337 I propose the following definitions: i. An empty record is: 1. A class without member. Or 2. An array of empty records. Or 3. A class with empty records. ii. An empty record type for parameter passing is POD for the purpose of layout and 1. A class without member. Or 2. A class with empty records.
GCC 6.1 has been released.
GCC 6.3 is being released, adjusting target milestone.
GCC 6.4 is being released, adjusting target milestone.
*** Bug 82693 has been marked as a duplicate of this bug. ***
Author: mpolacek Date: Wed Nov 22 16:06:18 2017 New Revision: 255066 URL: https://gcc.gnu.org/viewcvs?rev=255066&root=gcc&view=rev Log: PR c++/60336 PR middle-end/67239 PR target/68355 * c-decl.c (grokdeclarator): Set DECL_PADDING_P on unnamed bit-fields. * class.c (layout_class_type): Set DECL_PADDING_P on padding. * decl.c (cxx_init_decl_processing): Set TRANSLATION_UNIT_WARN_EMPTY_P. (grokdeclarator): Set DECL_PADDING_P on unnamed bit-fields. * lto.c (compare_tree_sccs_1): Compare TYPE_EMPTY_P and DECL_PADDING_P. * calls.c (initialize_argument_information): Call warn_parameter_passing_abi target hook. (store_one_arg): Use 0 for empty record size. Don't push 0 size argument onto stack. (must_pass_in_stack_var_size_or_pad): Return false for empty types. * common.opt: Update -fabi-version description. * config/i386/i386.c (init_cumulative_args): Set cum->warn_empty. (ix86_gimplify_va_arg): Call arg_int_size_in_bytes instead of int_size_in_bytes. (ix86_is_empty_record): New function. (ix86_warn_parameter_passing_abi): New function. (TARGET_EMPTY_RECORD_P): Redefine. (TARGET_WARN_PARAMETER_PASSING_ABI): Redefine. * config/i386/i386.h (CUMULATIVE_ARGS): Add warn_empty. * doc/tm.texi: Regenerated. * doc/tm.texi.in (TARGET_EMPTY_RECORD_P, TARGET_WARN_PARAMETER_PASSING_ABI): Add. * dwarf2out.c (get_ultimate_context): Move to tree.c. * explow.c (hard_function_value): Call arg_int_size_in_bytes instead of int_size_in_bytes. * expr.c (copy_blkmode_to_reg): Likewise. * function.c (aggregate_value_p): Return 0 for empty types. (assign_parm_find_entry_rtl): Call warn_parameter_passing_abi target hook. (locate_and_pad_parm): Call arg size_in_bytes instead size_in_bytes. * lto-streamer-out.c (hash_tree): Hash TYPE_EMPTY_P and DECL_PADDING_P. * stor-layout.c (finalize_type_size): Set TYPE_EMPTY_P. * target.def (empty_record_p, warn_parameter_passing_abi): New target hooks. * targhooks.c (hook_void_CUMULATIVE_ARGS_tree): New hook. (std_gimplify_va_arg_expr): Skip empty records. Call arg_size_in_bytes instead size_in_bytes. * targhooks.h (hook_void_CUMULATIVE_ARGS_tree): Declare. * tree-core.h (tree_type_common): Add empty_flag. (tree_decl_common): Update comments. * tree-streamer-in.c (unpack_ts_decl_common_value_fields): Stream DECL_PADDING_P. (unpack_ts_type_common_value_fields): Stream TYPE_EMPTY_P. * tree-streamer-out.c (pack_ts_decl_common_value_fields): Stream DECL_PADDING_P. (pack_ts_type_common_value_fields): Stream TYPE_EMPTY_P. * tree.c (default_is_empty_type): New function. (default_is_empty_record): New function. (arg_int_size_in_bytes): New function. (arg_size_in_bytes): New function. (get_ultimate_context): New function. * tree.h: Define TYPE_EMPTY_P, DECL_PADDING_P and TRANSLATION_UNIT_WARN_EMPTY_P. (default_is_empty_record, arg_int_size_in_bytes, arg_size_in_bytes, get_ultimate_context): Declare. * g++.dg/abi/empty12.C: New test. * g++.dg/abi/empty12.h: New test. * g++.dg/abi/empty12a.c: New test. * g++.dg/abi/empty13.C: New test. * g++.dg/abi/empty13.h: New test. * g++.dg/abi/empty13a.c: New test. * g++.dg/abi/empty14.C: New test. * g++.dg/abi/empty14.h: New test. * g++.dg/abi/empty14a.c: New test. * g++.dg/abi/empty15.C: New test. * g++.dg/abi/empty15.h: New test. * g++.dg/abi/empty15a.c: New test. * g++.dg/abi/empty16.C: New test. * g++.dg/abi/empty16.h: New test. * g++.dg/abi/empty16a.c: New test. * g++.dg/abi/empty17.C: New test. * g++.dg/abi/empty17.h: New test. * g++.dg/abi/empty17a.c: New test. * g++.dg/abi/empty18.C: New test. * g++.dg/abi/empty18.h: New test. * g++.dg/abi/empty18a.c: New test. * g++.dg/abi/empty19.C: New test. * g++.dg/abi/empty19.h: New test. * g++.dg/abi/empty19a.c: New test. * g++.dg/abi/empty20.C: New test. * g++.dg/abi/empty21.C: New test. * g++.dg/abi/empty22.C: New test. * g++.dg/abi/empty22.h: New test. * g++.dg/abi/empty22a.c: New test. * g++.dg/abi/empty23.C: New test. * g++.dg/abi/empty24.C: New test. * g++.dg/abi/empty25.C: New test. * g++.dg/abi/empty25.h: New test. * g++.dg/abi/empty25a.c: New test. * g++.dg/abi/empty26.C: New test. * g++.dg/abi/empty26.h: New test. * g++.dg/abi/empty26a.c: New test. * g++.dg/abi/empty27.C: New test. * g++.dg/abi/empty28.C: New test. * g++.dg/abi/pr60336-1.C: New test. * g++.dg/abi/pr60336-10.C: New test. * g++.dg/abi/pr60336-11.C: New test. * g++.dg/abi/pr60336-12.C: New test. * g++.dg/abi/pr60336-2.C: New test. * g++.dg/abi/pr60336-3.C: New test. * g++.dg/abi/pr60336-4.C: New test. * g++.dg/abi/pr60336-5.C: New test. * g++.dg/abi/pr60336-6.C: New test. * g++.dg/abi/pr60336-7.C: New test. * g++.dg/abi/pr60336-8.C: New test. * g++.dg/abi/pr60336-9.C: New test. * g++.dg/abi/pr68355.C: New test. * g++.dg/lto/pr60336_0.C: New test. Added: trunk/gcc/testsuite/g++.dg/abi/empty12.C trunk/gcc/testsuite/g++.dg/abi/empty12.h trunk/gcc/testsuite/g++.dg/abi/empty12a.c trunk/gcc/testsuite/g++.dg/abi/empty13.C trunk/gcc/testsuite/g++.dg/abi/empty13.h trunk/gcc/testsuite/g++.dg/abi/empty13a.c trunk/gcc/testsuite/g++.dg/abi/empty14.C trunk/gcc/testsuite/g++.dg/abi/empty14.h trunk/gcc/testsuite/g++.dg/abi/empty14a.c trunk/gcc/testsuite/g++.dg/abi/empty15.C trunk/gcc/testsuite/g++.dg/abi/empty15.h trunk/gcc/testsuite/g++.dg/abi/empty15a.c trunk/gcc/testsuite/g++.dg/abi/empty16.C trunk/gcc/testsuite/g++.dg/abi/empty16.h trunk/gcc/testsuite/g++.dg/abi/empty16a.c trunk/gcc/testsuite/g++.dg/abi/empty17.C trunk/gcc/testsuite/g++.dg/abi/empty17.h trunk/gcc/testsuite/g++.dg/abi/empty17a.c trunk/gcc/testsuite/g++.dg/abi/empty18.C trunk/gcc/testsuite/g++.dg/abi/empty18.h trunk/gcc/testsuite/g++.dg/abi/empty18a.c trunk/gcc/testsuite/g++.dg/abi/empty19.C trunk/gcc/testsuite/g++.dg/abi/empty19.h trunk/gcc/testsuite/g++.dg/abi/empty19a.c trunk/gcc/testsuite/g++.dg/abi/empty20.C trunk/gcc/testsuite/g++.dg/abi/empty21.C trunk/gcc/testsuite/g++.dg/abi/empty22.C trunk/gcc/testsuite/g++.dg/abi/empty22.h trunk/gcc/testsuite/g++.dg/abi/empty22a.c trunk/gcc/testsuite/g++.dg/abi/empty23.C trunk/gcc/testsuite/g++.dg/abi/empty24.C trunk/gcc/testsuite/g++.dg/abi/empty25.C trunk/gcc/testsuite/g++.dg/abi/empty25.h trunk/gcc/testsuite/g++.dg/abi/empty25a.c trunk/gcc/testsuite/g++.dg/abi/empty26.C trunk/gcc/testsuite/g++.dg/abi/empty26.h trunk/gcc/testsuite/g++.dg/abi/empty26a.c trunk/gcc/testsuite/g++.dg/abi/empty27.C trunk/gcc/testsuite/g++.dg/abi/empty28.C trunk/gcc/testsuite/g++.dg/abi/pr60336-1.C trunk/gcc/testsuite/g++.dg/abi/pr60336-10.C trunk/gcc/testsuite/g++.dg/abi/pr60336-11.C trunk/gcc/testsuite/g++.dg/abi/pr60336-12.C trunk/gcc/testsuite/g++.dg/abi/pr60336-2.C trunk/gcc/testsuite/g++.dg/abi/pr60336-3.C trunk/gcc/testsuite/g++.dg/abi/pr60336-4.C trunk/gcc/testsuite/g++.dg/abi/pr60336-5.C trunk/gcc/testsuite/g++.dg/abi/pr60336-6.C trunk/gcc/testsuite/g++.dg/abi/pr60336-7.C trunk/gcc/testsuite/g++.dg/abi/pr60336-8.C trunk/gcc/testsuite/g++.dg/abi/pr60336-9.C trunk/gcc/testsuite/g++.dg/abi/pr68355.C trunk/gcc/testsuite/g++.dg/lto/pr60336_0.C Modified: trunk/gcc/ChangeLog trunk/gcc/c/ChangeLog trunk/gcc/c/c-decl.c trunk/gcc/calls.c trunk/gcc/common.opt trunk/gcc/config/i386/i386.c trunk/gcc/config/i386/i386.h trunk/gcc/cp/ChangeLog trunk/gcc/cp/class.c trunk/gcc/cp/decl.c trunk/gcc/doc/tm.texi trunk/gcc/doc/tm.texi.in trunk/gcc/dwarf2out.c trunk/gcc/explow.c trunk/gcc/expr.c trunk/gcc/function.c trunk/gcc/lto-streamer-out.c trunk/gcc/lto/ChangeLog trunk/gcc/lto/lto.c trunk/gcc/stor-layout.c trunk/gcc/target.def trunk/gcc/targhooks.c trunk/gcc/targhooks.h trunk/gcc/testsuite/ChangeLog trunk/gcc/tree-core.h trunk/gcc/tree-streamer-in.c trunk/gcc/tree-streamer-out.c trunk/gcc/tree.c trunk/gcc/tree.h
Implemented for GCC 8.
*** Bug 69846 has been marked as a duplicate of this bug. ***
This is a pure C++ testcase that gives the wrong answer in GCC 7, presumably because libc's vsnprintf is expecting the C calling convention. struct foo { }; char buf[128]; int MySnprintf(struct foo, const char *pFormat, ...) { __builtin_va_list arguments; __builtin_va_start(arguments, pFormat); __builtin_vsnprintf(buf, sizeof(buf), pFormat, arguments); __builtin_va_end(arguments); return 0; } int main(void) { struct foo blah; MySnprintf(blah, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); __builtin_puts(buf); } GCC 7 prints: 1:2:3:4:5:4195847:6:7:8:9 GCC 8.1.0 prints: 1:2:3:4:5:6:7:8:9:10
Oh, and for completeness, GCC 8.1.0 -fabi-version=11 also prints a garbage value of course: 1:2:3:4:5:4195842:6:7:8:9