Adjust empty class parameter passing ABI (PR c++/60336)
Marek Polacek
polacek@redhat.com
Fri Oct 27 10:17:00 GMT 2017
This is my attempt at the empty class ABI change. To recap quickly, the C++
compiler has used a different calling convention for passing empty classes,
because C++ says they have size 1, while the GCC C extension gives them size 0.
But this difference doesn't mean that they need to be passed differently; in
either case there's no actual data involved.
I've made use of all the previous patches:
<https://gcc.gnu.org/ml/gcc-patches/2015-11/msg02064.html>
<https://gcc.gnu.org/ml/gcc-patches/2016-03/msg00183.html>
<https://gcc.gnu.org/ml/gcc-patches/2016-04/msg00559.html>
but this approach uses two target hooks which check whether a type is an empty
type according to the x86_64 psABI, and the second implements the warning for it.
It also uses a lang hook to determine whether to print a -Wabi warning. The
new passing can be turned back into the old passing using -fabi-version=11. So
I had to use the new langhook, otherwise I wouldn't be able make it dependent
on the C++ ABI verison.
Some earlier comments from Jason:
> I'm still uneasy about how much this requires generic code to think
> about empty types specifically. I'd prefer to encapsulate this as
> much as possible. Rather than places saying (empty ? 0 :
> int_size_in_bytes), I figured that would all be hidden in the target
> code, along with the warning. Places where you currently emit a
> warning from generic code ought to come from a target hook, either an
> existing one or a new one called something like
> warn_parameter_passing_abi.
I hope I've improved this now. I've introduced two new wrappers,
maybe_empty_type_size, and int_maybe_empty_type_size. I've moved the
warning to its own target hook with a new field in CUMULATIVE_ARGS; that
seems to work well.
> Note that nothing in gcc/ currently refers to warn_abi or warn_psabi,
> which are both c-common flags; some targets refer to warn_psabi.
True.
> > It also uses a lang hook to determine
> > whether to print a -Wabi warning. The new passing can be turned back into
> > the old passing using -fabi-version=11. So I had to use a new langhook,
> > otherwise I couldn't make it dependent on the C++ ABI verison.
>
> Hmm, that's unfortunate.
It is, but the possibility of using -fabi-version=11 to revert to the old
behavior seems useful.
> > The users will get the new empty records passing even with -m32.
>
> I thought we only wanted to make the change for -m64; -m32 is a legacy
> ABI at this point, it doesn't seem advisable to change it.
Sure thing, done (using the TARGET_64BIT check in ix86_is_empty_record_p).
> > One thing I wasn't sure about is what to do with a struct that contains
> > a zero-length array. We should consider it an empty type, but my
> > is_empty_record_p won't do that, the reason is that there doesn't seem
> > to be any change regarding passing: older GCCs wouldn't pass such a struct
> > via stack. So there's no warning for that, either.
>
> Not warning for that makes sense. Doing that by making
> is_empty_record_p give the wrong answer seems unwise.
is_empty_record_p should now give the correct answer. As I said yesterday,
I think we shouldn't consider struct S { struct { } a; int b[0]; } as empty,
because [0] is a GNU extension and struct S { struct { } a; int b[]; } is
considered non-empty. Also, for the record, the struct with the flexible array
member [0] broke struct-layout when considered empty. Thus the uglyfying of
is_empty_record_p. empty23.C and empty24.C test that.
> Also, you need to handled unnamed bit-fields, as they aren't data members.
Here's another hiccup: e.g. struct S { unsigned : 15; }; are considered empty,
but they were considered empty even before my change (so we probably shouldn't
warn), with "unsigned : 0;" it's different and there seems to be a change in
how we pass that. So I kept the warning. Not sure how important this is.
Any other concerns?
Bootstrapped/regtested on x86_64-linux, ppc64-linux, and aarch64-linux.
2017-10-27 Marek Polacek <polacek@redhat.com>
H.J. Lu <hongjiu.lu@intel.com>
Jason Merrill <jason@redhat.com>
PR c++/60336
PR middle-end/67239
PR target/68355
* 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_function_arg_advance): Skip empty records.
(ix86_return_in_memory): Return false for empty types.
(ix86_gimplify_va_arg): Call int_maybe_empty_type_size instead of
int_size_in_bytes.
(ix86_is_empty_record_p): 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.
* explow.c (hard_function_value): Call int_maybe_empty_type_size
instead of int_size_in_bytes.
* expr.c (copy_blkmode_to_reg): Likewise.
* function.c (assign_parm_find_entry_rtl): Call
warn_parameter_passing_abi target hook.
(locate_and_pad_parm): Call maybe_empty_type_size instead
size_in_bytes.
* langhooks-def.h (LANG_HOOKS_WARN_EMPTY_CLASS_ABI): Define.
* langhooks.h (struct lang_hooks): Add warn_empty_class_abi.
* target.def (empty_record_p, warn_parameter_passing_abi): New target
hook.
* targhooks.c (hook_void_CUMULATIVE_ARGS_tree): New hook.
(std_gimplify_va_arg_expr): Skip empty records. Call
maybe_empty_type_size instead size_in_bytes.
* targhooks.h (hook_void_CUMULATIVE_ARGS_tree): Declare.
* tree.c (array_type_nelts_top): New function.
(is_empty_type): New function.
(is_empty_record_p): New function.
(int_maybe_empty_type_size): New function.
(maybe_empty_type_size): New function.
* tree.h (array_type_nelts_top, is_empty_record_p,
int_maybe_empty_type_size, maybe_empty_type_size): Declare.
* cp-lang.c: Include "flags.h".
(LANG_HOOKS_WARN_EMPTY_CLASS_ABI): Redefine.
(cxx_warn_empty_class_abi): New function.
* cp-tree.h (array_type_nelts_top): Remove declaration.
* tree.c (array_type_nelts_top): Move to tree.c.
* testsuite/g++.dg/abi/empty12.C: New test.
* testsuite/g++.dg/abi/empty12.h: New test.
* testsuite/g++.dg/abi/empty12a.c: New test.
* testsuite/g++.dg/abi/empty13.C: New test.
* testsuite/g++.dg/abi/empty13.h: New test.
* testsuite/g++.dg/abi/empty13a.c: New test.
* testsuite/g++.dg/abi/empty14.C: New test.
* testsuite/g++.dg/abi/empty14.h: New test.
* testsuite/g++.dg/abi/empty14a.c: New test.
* testsuite/g++.dg/abi/empty15.C: New test.
* testsuite/g++.dg/abi/empty15.h: New test.
* testsuite/g++.dg/abi/empty15a.c: New test.
* testsuite/g++.dg/abi/empty16.C: New test.
* testsuite/g++.dg/abi/empty16.h: New test.
* testsuite/g++.dg/abi/empty16a.c: New test.
* testsuite/g++.dg/abi/empty17.C: New test.
* testsuite/g++.dg/abi/empty17.h: New test.
* testsuite/g++.dg/abi/empty17a.c: New test.
* testsuite/g++.dg/abi/empty18.C: New test.
* testsuite/g++.dg/abi/empty18.h: New test.
* testsuite/g++.dg/abi/empty18a.c: New test.
* testsuite/g++.dg/abi/empty19.C: New test.
* testsuite/g++.dg/abi/empty19.h: New test.
* testsuite/g++.dg/abi/empty19a.c: New test.
* testsuite/g++.dg/abi/empty20.C: New test.
* testsuite/g++.dg/abi/empty21.C: New test.
* testsuite/g++.dg/abi/empty22.C: New test.
* testsuite/g++.dg/abi/empty22.h: New test.
* testsuite/g++.dg/abi/empty22a.c: New test.
* testsuite/g++.dg/abi/empty23.C: New test.
* testsuite/g++.dg/abi/empty24.C: New test.
* testsuite/g++.dg/abi/pr60336-1.C: New test.
* testsuite/g++.dg/abi/pr60336-10.C: New test.
* testsuite/g++.dg/abi/pr60336-11.C: New test.
* testsuite/g++.dg/abi/pr60336-12.C: New test.
* testsuite/g++.dg/abi/pr60336-2.C: New test.
* testsuite/g++.dg/abi/pr60336-3.C: New test.
* testsuite/g++.dg/abi/pr60336-4.C: New test.
* testsuite/g++.dg/abi/pr60336-5.C: New test.
* testsuite/g++.dg/abi/pr60336-6.C: New test.
* testsuite/g++.dg/abi/pr60336-7.C: New test.
* testsuite/g++.dg/abi/pr60336-8.C: New test.
* testsuite/g++.dg/abi/pr60336-9.C: New test.
* testsuite/g++.dg/abi/pr68355.C: New test.
diff --git gcc/calls.c gcc/calls.c
index 3730f43c7a9..29f547942d8 100644
--- gcc/calls.c
+++ gcc/calls.c
@@ -1850,6 +1850,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].unsignedp = unsignedp;
args[i].mode = mode;
+ targetm.calls.warn_parameter_passing_abi (args_so_far, type);
+
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
@@ -5358,7 +5360,11 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
Note that in C the default argument promotions
will prevent such mismatches. */
- size = GET_MODE_SIZE (arg->mode);
+ if (targetm.calls.empty_record_p (TREE_TYPE (pval)))
+ size = 0;
+ else
+ size = GET_MODE_SIZE (arg->mode);
+
/* Compute how much space the push instruction will push.
On many machines, pushing a byte will advance the stack
pointer by a halfword. */
@@ -5390,10 +5396,12 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
/* This isn't already where we want it on the stack, so put it there.
This can either be done with push or copy insns. */
- if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
- parm_align, partial, reg, used - size, argblock,
- ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+ if (used
+ && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+ NULL_RTX, parm_align, partial, reg, used - size,
+ argblock, ARGS_SIZE_RTX (arg->locate.offset),
+ reg_parm_stack_space,
+ ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
sibcall_failure = 1;
/* Unless this is a partially-in-register argument, the argument is now
@@ -5426,9 +5434,9 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
/* PUSH_ROUNDING has no effect on us, because emit_push_insn
for BLKmode is careful to avoid it. */
excess = (arg->locate.size.constant
- - int_size_in_bytes (TREE_TYPE (pval))
+ - int_maybe_empty_type_size (TREE_TYPE (pval))
+ partial);
- size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+ size_rtx = expand_expr (maybe_empty_type_size (TREE_TYPE (pval)),
NULL_RTX, TYPE_MODE (sizetype),
EXPAND_NORMAL);
}
@@ -5504,10 +5512,12 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
}
}
- emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
- parm_align, partial, reg, excess, argblock,
- ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+ if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+ emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
+ parm_align, partial, reg, excess, argblock,
+ ARGS_SIZE_RTX (arg->locate.offset),
+ reg_parm_stack_space,
+ ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
/* Unless this is a partially-in-register argument, the argument is now
in the stack.
@@ -5585,6 +5595,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
if (TREE_ADDRESSABLE (type))
return true;
+ if (targetm.calls.empty_record_p (type))
+ return false;
+
/* If the padding and mode of the type is such that a copy into
a register would put it into the wrong part of the register. */
if (mode == BLKmode
diff --git gcc/common.opt gcc/common.opt
index 836f05b95a2..41d97ae7475 100644
--- gcc/common.opt
+++ gcc/common.opt
@@ -932,7 +932,7 @@ Driver Undocumented
; Default in G++ 7.
;
; 12: Corrects the calling convention for classes with only deleted copy/move
-; constructors.
+; constructors and changes passing/returning of empty records.
; Default in G++ 8.
;
; Additional positive integers will be assigned as new versions of
diff --git gcc/config/i386/i386.c gcc/config/i386/i386.c
index 1facf121987..c7058ac5638 100644
--- gcc/config/i386/i386.c
+++ gcc/config/i386/i386.c
@@ -7186,6 +7186,26 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
cum->force_bnd_pass = 0;
cum->decl = fndecl;
+ cum->warn_empty = !warn_abi || cum->stdarg;
+ if (!cum->warn_empty && fntype)
+ {
+ function_args_iterator iter;
+ tree argtype;
+ bool seen_empty_type = false;
+ FOREACH_FUNCTION_ARGS (fntype, argtype, iter)
+ {
+ if (VOID_TYPE_P (argtype))
+ break;
+ if (targetm.calls.empty_record_p (argtype))
+ seen_empty_type = true;
+ else if (seen_empty_type)
+ {
+ cum->warn_empty = true;
+ break;
+ }
+ }
+ }
+
if (!TARGET_64BIT)
{
/* If there are variable arguments, then we won't pass anything
@@ -8327,6 +8347,10 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
return;
+ /* Skip empty records because they won't be passed. */
+ if (type && targetm.calls.empty_record_p (type))
+ return;
+
if (mode == BLKmode)
bytes = int_size_in_bytes (type);
else
@@ -9293,6 +9317,10 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
if (POINTER_BOUNDS_TYPE_P (type))
return false;
+ /* Empty records are never passed in memory. */
+ if (type && targetm.calls.empty_record_p (type))
+ return false;
+
if (TARGET_64BIT)
{
if (ix86_function_type_abi (fntype) == MS_ABI)
@@ -9873,7 +9901,7 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
if (indirect_p)
type = build_pointer_type (type);
- size = int_size_in_bytes (type);
+ size = int_maybe_empty_type_size (type);
rsize = CEIL (size, UNITS_PER_WORD);
nat_mode = type_natural_mode (type, NULL, false);
@@ -28772,6 +28800,44 @@ ix86_constant_alignment (const_tree exp, HOST_WIDE_INT align)
return align;
}
+/* Implement TARGET_EMPTY_RECORD_P. */
+
+static bool
+ix86_is_empty_record_p (const_tree type)
+{
+ if (!TARGET_64BIT)
+ return false;
+ return is_empty_record_p (type);
+}
+
+/* Implement TARGET_WARN_PARAMETER_PASSING_ABI. */
+
+static void
+ix86_warn_parameter_passing_abi (cumulative_args_t cum_v, tree type)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+ if (!cum->warn_empty)
+ return;
+
+ if (!targetm.calls.empty_record_p (type))
+ return;
+
+ if (!lang_hooks.warn_empty_class_abi ())
+ return;
+
+ /* If the actual size of the type is zero, then there is no change
+ in how objects of this size are passed. */
+ if (int_size_in_bytes (type) == 0)
+ return;
+
+ warning (OPT_Wabi, "empty class %qT parameter passing ABI "
+ "changes in -fabi-version=12 (GCC 8)", type);
+
+ /* Only warn once. */
+ cum->warn_empty = false;
+}
+
/* Compute the alignment for a variable for Intel MCU psABI. TYPE is
the data type, and ALIGN is the alignment that the object would
ordinarily have. */
@@ -50311,6 +50377,12 @@ ix86_run_selftests (void)
#undef TARGET_CONSTANT_ALIGNMENT
#define TARGET_CONSTANT_ALIGNMENT ix86_constant_alignment
+#undef TARGET_EMPTY_RECORD_P
+#define TARGET_EMPTY_RECORD_P ix86_is_empty_record_p
+
+#undef TARGET_WARN_PARAMETER_PASSING_ABI
+#define TARGET_WARN_PARAMETER_PASSING_ABI ix86_warn_parameter_passing_abi
+
#if CHECKING_P
#undef TARGET_RUN_TARGET_SELFTESTS
#define TARGET_RUN_TARGET_SELFTESTS selftest::ix86_run_selftests
diff --git gcc/config/i386/i386.h gcc/config/i386/i386.h
index 837906b5169..7f5d245b568 100644
--- gcc/config/i386/i386.h
+++ gcc/config/i386/i386.h
@@ -1633,6 +1633,8 @@ typedef struct ix86_args {
int warn_avx; /* True when we want to warn about AVX ABI. */
int warn_sse; /* True when we want to warn about SSE ABI. */
int warn_mmx; /* True when we want to warn about MMX ABI. */
+ int warn_empty; /* True when we want to warn about empty classes
+ passing ABI change. */
int sse_regno; /* next available sse register number */
int mmx_words; /* # mmx words passed so far */
int mmx_nregs; /* # mmx registers available for passing */
diff --git gcc/cp/cp-lang.c gcc/cp/cp-lang.c
index 805319a4185..78336730a94 100644
--- gcc/cp/cp-lang.c
+++ gcc/cp/cp-lang.c
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "langhooks-def.h"
#include "cp-objcp-common.h"
+#include "flags.h"
enum c_language_kind c_language = clk_cxx;
static void cp_init_ts (void);
@@ -35,6 +36,7 @@ static tree cp_eh_personality (void);
static tree get_template_innermost_arguments_folded (const_tree);
static tree get_template_argument_pack_elems_folded (const_tree);
static tree cxx_enum_underlying_base_type (const_tree);
+static bool cxx_warn_empty_class_abi (void);
/* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h;
consequently, there should be very few hooks below. */
@@ -78,6 +80,8 @@ static tree cxx_enum_underlying_base_type (const_tree);
#define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type
#undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE
#define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type
+#undef LANG_HOOKS_WARN_EMPTY_CLASS_ABI
+#define LANG_HOOKS_WARN_EMPTY_CLASS_ABI cxx_warn_empty_class_abi
#if CHECKING_P
#undef LANG_HOOKS_RUN_LANG_SELFTESTS
@@ -234,6 +238,15 @@ tree cxx_enum_underlying_base_type (const_tree type)
return underlying_type;
}
+/* Return true if we should warn about the change in empty class parameter
+ passing ABI. */
+
+static bool
+cxx_warn_empty_class_abi (void)
+{
+ return warn_abi && abi_version_crosses (12);
+}
+
#if CHECKING_P
namespace selftest {
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index f2570b00386..45f5d82be03 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6916,7 +6916,6 @@ extern tree canonical_eh_spec (tree);
extern tree build_exception_variant (tree, tree);
extern tree bind_template_template_parm (tree, tree);
extern tree array_type_nelts_total (tree);
-extern tree array_type_nelts_top (tree);
extern tree break_out_target_exprs (tree);
extern tree build_ctor_subob_ref (tree, tree, tree);
extern tree replace_placeholders (tree, tree, bool * = NULL);
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 48d40945af3..8a878d70e34 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2834,19 +2834,6 @@ cxx_print_statistics (void)
}
/* Return, as an INTEGER_CST node, the number of elements for TYPE
- (which is an ARRAY_TYPE). This counts only elements of the top
- array. */
-
-tree
-array_type_nelts_top (tree type)
-{
- return fold_build2_loc (input_location,
- PLUS_EXPR, sizetype,
- array_type_nelts (type),
- size_one_node);
-}
-
-/* Return, as an INTEGER_CST node, the number of elements for TYPE
(which is an ARRAY_TYPE). This one is a recursive count of all
ARRAY_TYPEs that are clumped together. */
diff --git gcc/doc/tm.texi gcc/doc/tm.texi
index c02f4d35116..9c4405a4cb5 100644
--- gcc/doc/tm.texi
+++ gcc/doc/tm.texi
@@ -4548,6 +4548,16 @@ This target hook returns the mode to be used when accessing raw return registers
This target hook returns the mode to be used when accessing raw argument registers in @code{__builtin_apply_args}. Define this macro if the value in @var{reg_raw_mode} is not correct.
@end deftypefn
+@deftypefn {Target Hook} bool TARGET_EMPTY_RECORD_P (const_tree @var{type})
+This target hook returns true if the type is an empty record. The default
+is to return @code{false}.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_WARN_PARAMETER_PASSING_ABI (cumulative_args_t @var{ca}, tree @var{type})
+This target hook warns about the change in empty class parameter passing
+ABI.
+@end deftypefn
+
@node Caller Saves
@subsection Caller-Saves Register Allocation
diff --git gcc/doc/tm.texi.in gcc/doc/tm.texi.in
index 37308e1e551..8b6b64f542f 100644
--- gcc/doc/tm.texi.in
+++ gcc/doc/tm.texi.in
@@ -3437,6 +3437,10 @@ nothing when you use @option{-freg-struct-return} mode.
@hook TARGET_GET_RAW_ARG_MODE
+@hook TARGET_EMPTY_RECORD_P
+
+@hook TARGET_WARN_PARAMETER_PASSING_ABI
+
@node Caller Saves
@subsection Caller-Saves Register Allocation
diff --git gcc/explow.c gcc/explow.c
index 662865d2808..5c864fedd1b 100644
--- gcc/explow.c
+++ gcc/explow.c
@@ -2166,7 +2166,7 @@ hard_function_value (const_tree valtype, const_tree func, const_tree fntype,
if (REG_P (val)
&& GET_MODE (val) == BLKmode)
{
- unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
+ unsigned HOST_WIDE_INT bytes = int_maybe_empty_type_size (valtype);
opt_scalar_int_mode tmpmode;
/* int_size_in_bytes can return -1. We don't need a check here
diff --git gcc/expr.c gcc/expr.c
index 496d492c9fa..337bd6de579 100644
--- gcc/expr.c
+++ gcc/expr.c
@@ -2746,7 +2746,7 @@ copy_blkmode_to_reg (machine_mode mode, tree src)
x = expand_normal (src);
- bytes = int_size_in_bytes (TREE_TYPE (src));
+ bytes = int_maybe_empty_type_size (TREE_TYPE (src));
if (bytes == 0)
return NULL_RTX;
diff --git gcc/function.c gcc/function.c
index 339419ee1da..b7f97bf0ae5 100644
--- gcc/function.c
+++ gcc/function.c
@@ -2528,6 +2528,9 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
return;
}
+ targetm.calls.warn_parameter_passing_abi (all->args_so_far,
+ data->passed_type);
+
entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
data->promoted_mode,
data->passed_type,
@@ -4140,8 +4143,9 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
- sizetree
- = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+ sizetree = (type
+ ? maybe_empty_type_size (type)
+ : size_int (GET_MODE_SIZE (passed_mode)));
where_pad = targetm.calls.function_arg_padding (passed_mode, type);
boundary = targetm.calls.function_arg_boundary (passed_mode, type);
round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
diff --git gcc/langhooks-def.h gcc/langhooks-def.h
index 61b081bd7cc..4cb31a68d3d 100644
--- gcc/langhooks-def.h
+++ gcc/langhooks-def.h
@@ -132,6 +132,7 @@ extern int lhd_type_dwarf_attribute (const_tree, int);
#define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS false
#define LANG_HOOKS_RUN_LANG_SELFTESTS lhd_do_nothing
#define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location
+#define LANG_HOOKS_WARN_EMPTY_CLASS_ABI hook_bool_void_false
/* Attribute hooks. */
#define LANG_HOOKS_ATTRIBUTE_TABLE NULL
@@ -344,7 +345,8 @@ extern void lhd_end_section (void);
LANG_HOOKS_DEEP_UNSHARING, \
LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS, \
LANG_HOOKS_RUN_LANG_SELFTESTS, \
- LANG_HOOKS_GET_SUBSTRING_LOCATION \
+ LANG_HOOKS_GET_SUBSTRING_LOCATION, \
+ LANG_HOOKS_WARN_EMPTY_CLASS_ABI \
}
#endif /* GCC_LANG_HOOKS_DEF_H */
diff --git gcc/langhooks.h gcc/langhooks.h
index d1288f1965d..ed79b2d8626 100644
--- gcc/langhooks.h
+++ gcc/langhooks.h
@@ -538,6 +538,9 @@ struct lang_hooks
const char *(*get_substring_location) (const substring_loc &,
location_t *out_loc);
+ /* Return true when we should warn about the empty class ABI change. */
+ bool (*warn_empty_class_abi) (void);
+
/* Whenever you add entries here, make sure you adjust langhooks-def.h
and langhooks.c accordingly. */
};
diff --git gcc/target.def gcc/target.def
index 6a1cd31f2ac..9445a412689 100644
--- gcc/target.def
+++ gcc/target.def
@@ -5043,6 +5043,22 @@ DEFHOOK
machine_mode, (int regno),
default_get_reg_raw_mode)
+/* Return true if a type is an empty record. */
+DEFHOOK
+(empty_record_p,
+ "This target hook returns true if the type is an empty record. The default\n\
+is to return @code{false}.",
+ bool, (const_tree type),
+ hook_bool_const_tree_false)
+
+/* Warn about the change in empty class parameter passing ABI. */
+DEFHOOK
+(warn_parameter_passing_abi,
+ "This target hook warns about the change in empty class parameter passing\n\
+ABI.",
+ void, (cumulative_args_t ca, tree type),
+ hook_void_CUMULATIVE_ARGS_tree)
+
HOOK_VECTOR_END (calls)
DEFHOOK
diff --git gcc/targhooks.c gcc/targhooks.c
index 92ecc90d4d4..f117382d565 100644
--- gcc/targhooks.c
+++ gcc/targhooks.c
@@ -734,6 +734,12 @@ hook_int_CUMULATIVE_ARGS_mode_tree_bool_0 (
}
void
+hook_void_CUMULATIVE_ARGS_tree (cumulative_args_t ca ATTRIBUTE_UNUSED,
+ tree ATTRIBUTE_UNUSED)
+{
+}
+
+void
default_function_arg_advance (cumulative_args_t ca ATTRIBUTE_UNUSED,
machine_mode mode ATTRIBUTE_UNUSED,
const_tree type ATTRIBUTE_UNUSED,
@@ -2084,6 +2090,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
/* va_list pointer is aligned to PARM_BOUNDARY. If argument actually
requires greater alignment, we must perform dynamic alignment. */
if (boundary > align
+ && !targetm.calls.empty_record_p (type)
&& !integer_zerop (TYPE_SIZE (type)))
{
t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -2110,7 +2117,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
}
/* Compute the rounded size of the type. */
- type_size = size_in_bytes (type);
+ type_size = maybe_empty_type_size (type);
rounded_size = round_up (type_size, align);
/* Reduce rounded_size so it's sharable with the postqueue. */
diff --git gcc/targhooks.h gcc/targhooks.h
index f60bca257f7..722608f35cc 100644
--- gcc/targhooks.h
+++ gcc/targhooks.h
@@ -134,6 +134,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
(cumulative_args_t, machine_mode, const_tree, bool);
extern int hook_int_CUMULATIVE_ARGS_mode_tree_bool_0
(cumulative_args_t, machine_mode, tree, bool);
+extern void hook_void_CUMULATIVE_ARGS_tree
+ (cumulative_args_t, tree);
extern const char *hook_invalid_arg_for_unprototyped_fn
(const_tree, const_tree, const_tree);
extern void default_function_arg_advance
diff --git gcc/testsuite/g++.dg/abi/empty12.C gcc/testsuite/g++.dg/abi/empty12.C
index e69de29bb2d..20d85ff873e 100644
--- gcc/testsuite/g++.dg/abi/empty12.C
+++ gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty12a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty12.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty12.h gcc/testsuite/g++.dg/abi/empty12.h
index e69de29bb2d..c61afcda0fb 100644
--- gcc/testsuite/g++.dg/abi/empty12.h
+++ gcc/testsuite/g++.dg/abi/empty12.h
@@ -0,0 +1,9 @@
+struct dummy { };
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty12a.c gcc/testsuite/g++.dg/abi/empty12a.c
index e69de29bb2d..34a25bad75d 100644
--- gcc/testsuite/g++.dg/abi/empty12a.c
+++ gcc/testsuite/g++.dg/abi/empty12a.c
@@ -0,0 +1,6 @@
+#include "empty12.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty13.C gcc/testsuite/g++.dg/abi/empty13.C
index e69de29bb2d..0cb9a373e35 100644
--- gcc/testsuite/g++.dg/abi/empty13.C
+++ gcc/testsuite/g++.dg/abi/empty13.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-x c -fabi-version=11" }
+// { dg-additional-sources "empty13a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty13.h"
+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;
+}
diff --git gcc/testsuite/g++.dg/abi/empty13.h gcc/testsuite/g++.dg/abi/empty13.h
index e69de29bb2d..c61afcda0fb 100644
--- gcc/testsuite/g++.dg/abi/empty13.h
+++ gcc/testsuite/g++.dg/abi/empty13.h
@@ -0,0 +1,9 @@
+struct dummy { };
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty13a.c gcc/testsuite/g++.dg/abi/empty13a.c
index e69de29bb2d..b4303a63826 100644
--- gcc/testsuite/g++.dg/abi/empty13a.c
+++ gcc/testsuite/g++.dg/abi/empty13a.c
@@ -0,0 +1,6 @@
+#include "empty13.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 == -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty14.C gcc/testsuite/g++.dg/abi/empty14.C
index e69de29bb2d..2868d8ad3f3 100644
--- gcc/testsuite/g++.dg/abi/empty14.C
+++ gcc/testsuite/g++.dg/abi/empty14.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty14a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty14.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty14.h gcc/testsuite/g++.dg/abi/empty14.h
index e69de29bb2d..5842279cf37 100644
--- gcc/testsuite/g++.dg/abi/empty14.h
+++ gcc/testsuite/g++.dg/abi/empty14.h
@@ -0,0 +1,10 @@
+struct dummy0 { };
+struct dummy { struct dummy0 d[140]; };
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty14a.c gcc/testsuite/g++.dg/abi/empty14a.c
index e69de29bb2d..8b3d7800c36 100644
--- gcc/testsuite/g++.dg/abi/empty14a.c
+++ gcc/testsuite/g++.dg/abi/empty14a.c
@@ -0,0 +1,6 @@
+#include "empty14.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty15.C gcc/testsuite/g++.dg/abi/empty15.C
index e69de29bb2d..12385f78c78 100644
--- gcc/testsuite/g++.dg/abi/empty15.C
+++ gcc/testsuite/g++.dg/abi/empty15.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty15a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty15.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty15.h gcc/testsuite/g++.dg/abi/empty15.h
index e69de29bb2d..1c6f26f5ae8 100644
--- gcc/testsuite/g++.dg/abi/empty15.h
+++ gcc/testsuite/g++.dg/abi/empty15.h
@@ -0,0 +1,30 @@
+struct A1 {};
+struct A2 {};
+struct B1 { struct A1 a; struct A2 b; };
+struct B2 { struct A1 a; struct A2 b; };
+struct C1 { struct B1 a; struct B2 b; };
+struct C2 { struct B1 a; struct B2 b; };
+struct D1 { struct C1 a; struct C2 b; };
+struct D2 { struct C1 a; struct C2 b; };
+struct E1 { struct D1 a; struct D2 b; };
+struct E2 { struct D1 a; struct D2 b; };
+struct F1 { struct E1 a; struct E2 b; };
+struct F2 { struct E1 a; struct E2 b; };
+struct G1 { struct F1 a; struct F2 b; };
+struct G2 { struct F1 a; struct F2 b; };
+struct H1 { struct G1 a; struct G2 b; };
+struct H2 { struct G1 a; struct G2 b; };
+struct I1 { struct H1 a; struct H2 b; };
+struct I2 { struct H1 a; struct H2 b; };
+struct J1 { struct I1 a; struct I2 b; };
+struct J2 { struct I1 a; struct I2 b; };
+struct dummy { struct J1 a; struct J2 b; };
+
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty15a.c gcc/testsuite/g++.dg/abi/empty15a.c
index e69de29bb2d..325b2c5ba09 100644
--- gcc/testsuite/g++.dg/abi/empty15a.c
+++ gcc/testsuite/g++.dg/abi/empty15a.c
@@ -0,0 +1,6 @@
+#include "empty15.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty16.C gcc/testsuite/g++.dg/abi/empty16.C
index e69de29bb2d..1ca52f9011e 100644
--- gcc/testsuite/g++.dg/abi/empty16.C
+++ gcc/testsuite/g++.dg/abi/empty16.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty16a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty16.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty16.h gcc/testsuite/g++.dg/abi/empty16.h
index e69de29bb2d..7552ae06576 100644
--- gcc/testsuite/g++.dg/abi/empty16.h
+++ gcc/testsuite/g++.dg/abi/empty16.h
@@ -0,0 +1,16 @@
+#ifdef __cplusplus
+struct A1 {};
+struct A2 {};
+struct dummy : A1, A2 {} ;
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty16a.c gcc/testsuite/g++.dg/abi/empty16a.c
index e69de29bb2d..6cb7fbccecc 100644
--- gcc/testsuite/g++.dg/abi/empty16a.c
+++ gcc/testsuite/g++.dg/abi/empty16a.c
@@ -0,0 +1,6 @@
+#include "empty16.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty17.C gcc/testsuite/g++.dg/abi/empty17.C
index e69de29bb2d..d386e5481af 100644
--- gcc/testsuite/g++.dg/abi/empty17.C
+++ gcc/testsuite/g++.dg/abi/empty17.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty17a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty17.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty17.h gcc/testsuite/g++.dg/abi/empty17.h
index e69de29bb2d..9cf72baca2e 100644
--- gcc/testsuite/g++.dg/abi/empty17.h
+++ gcc/testsuite/g++.dg/abi/empty17.h
@@ -0,0 +1,27 @@
+#ifdef __cplusplus
+struct A1
+{
+ void foo (void);
+ unsigned int : 15;
+};
+struct A2
+{
+ void bar (void);
+ unsigned int : 15;
+};
+struct dummy : A1, A2
+{
+ unsigned int : 15;
+};
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty17a.c gcc/testsuite/g++.dg/abi/empty17a.c
index e69de29bb2d..24408fde09c 100644
--- gcc/testsuite/g++.dg/abi/empty17a.c
+++ gcc/testsuite/g++.dg/abi/empty17a.c
@@ -0,0 +1,6 @@
+#include "empty17.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty18.C gcc/testsuite/g++.dg/abi/empty18.C
index e69de29bb2d..be69c6a2115 100644
--- gcc/testsuite/g++.dg/abi/empty18.C
+++ gcc/testsuite/g++.dg/abi/empty18.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty18a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty18.h"
+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;
+}
diff --git gcc/testsuite/g++.dg/abi/empty18.h gcc/testsuite/g++.dg/abi/empty18.h
index e69de29bb2d..86e7ecdd211 100644
--- gcc/testsuite/g++.dg/abi/empty18.h
+++ gcc/testsuite/g++.dg/abi/empty18.h
@@ -0,0 +1,9 @@
+struct dummy { int d[0]; };
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty18a.c gcc/testsuite/g++.dg/abi/empty18a.c
index e69de29bb2d..902860bdc01 100644
--- gcc/testsuite/g++.dg/abi/empty18a.c
+++ gcc/testsuite/g++.dg/abi/empty18a.c
@@ -0,0 +1,6 @@
+#include "empty18.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty19.C gcc/testsuite/g++.dg/abi/empty19.C
index e69de29bb2d..84f5b75558b 100644
--- gcc/testsuite/g++.dg/abi/empty19.C
+++ gcc/testsuite/g++.dg/abi/empty19.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty19a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty19.h"
+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;
+}
diff --git gcc/testsuite/g++.dg/abi/empty19.h gcc/testsuite/g++.dg/abi/empty19.h
index e69de29bb2d..616b87bdd93 100644
--- gcc/testsuite/g++.dg/abi/empty19.h
+++ gcc/testsuite/g++.dg/abi/empty19.h
@@ -0,0 +1,10 @@
+struct dummy0 { };
+struct dummy { struct dummy0 d[0]; };
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty19a.c gcc/testsuite/g++.dg/abi/empty19a.c
index e69de29bb2d..767b1eb7320 100644
--- gcc/testsuite/g++.dg/abi/empty19a.c
+++ gcc/testsuite/g++.dg/abi/empty19a.c
@@ -0,0 +1,6 @@
+#include "empty19.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty20.C gcc/testsuite/g++.dg/abi/empty20.C
index e69de29bb2d..5022033f669 100644
--- gcc/testsuite/g++.dg/abi/empty20.C
+++ gcc/testsuite/g++.dg/abi/empty20.C
@@ -0,0 +1,19 @@
+// PR c++/60336
+// { dg-options "-Wabi=11 -O0" }
+
+struct A { };
+
+void f(A, A) { } // No warning, trailing parms all empty
+void f(A, A, int) { } // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+__attribute__ ((always_inline))
+inline void f(A a, int i) // No warning, always inlined
+{
+ f(a,a,i); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
+int main()
+{
+ A a;
+ f(a,a);
+ f(a,a,42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+ f(a,42);
+}
diff --git gcc/testsuite/g++.dg/abi/empty21.C gcc/testsuite/g++.dg/abi/empty21.C
index e69de29bb2d..3b2e3b836b1 100644
--- gcc/testsuite/g++.dg/abi/empty21.C
+++ gcc/testsuite/g++.dg/abi/empty21.C
@@ -0,0 +1,23 @@
+// PR c++/60336
+// { dg-options "-Wabi=11" }
+
+#include <stdarg.h>
+
+struct A { };
+
+void f(int i, ...)
+{
+ va_list ap;
+ va_start (ap, i);
+ if (i >= 1)
+ va_arg (ap, A);
+ if (i >= 2)
+ va_arg (ap, int);
+}
+
+int main()
+{
+ f(0);
+ f(1, A()); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+ f(2, A(), 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/empty22.C gcc/testsuite/g++.dg/abi/empty22.C
index e69de29bb2d..f4f4a02bf31 100644
--- gcc/testsuite/g++.dg/abi/empty22.C
+++ gcc/testsuite/g++.dg/abi/empty22.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-Wabi=11 -x c" }
+// { dg-additional-sources "empty22a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty22.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+ struct dummy d;
+ struct foo f = { -1, -2, -3, -4, -5 };
+
+ fun(d, f); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/empty22.h gcc/testsuite/g++.dg/abi/empty22.h
index e69de29bb2d..8d54dc74519 100644
--- gcc/testsuite/g++.dg/abi/empty22.h
+++ gcc/testsuite/g++.dg/abi/empty22.h
@@ -0,0 +1,27 @@
+#ifdef __cplusplus
+struct A1
+{
+ void foo (void);
+ unsigned int : 0;
+};
+struct A2
+{
+ void bar (void);
+ unsigned int : 0;
+};
+struct dummy : A1, A2
+{
+ unsigned int : 0;
+};
+#else
+struct dummy {};
+#endif
+
+struct foo
+{
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+};
diff --git gcc/testsuite/g++.dg/abi/empty22a.c gcc/testsuite/g++.dg/abi/empty22a.c
index e69de29bb2d..7606c524263 100644
--- gcc/testsuite/g++.dg/abi/empty22a.c
+++ gcc/testsuite/g++.dg/abi/empty22a.c
@@ -0,0 +1,6 @@
+#include "empty22.h"
+void fun(struct dummy d, struct foo f)
+{
+ if (f.i1 != -1)
+ __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/abi/empty23.C gcc/testsuite/g++.dg/abi/empty23.C
index e69de29bb2d..b97d2804529 100644
--- gcc/testsuite/g++.dg/abi/empty23.C
+++ gcc/testsuite/g++.dg/abi/empty23.C
@@ -0,0 +1,25 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=11" }
+
+struct S
+{
+ struct { } a;
+ __extension__ int b[0];
+};
+
+struct S s;
+struct S a[5];
+
+void
+foo (struct S, struct S *arg1, struct S)
+{
+ if (arg1 != &a[1])
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ foo (s, &a[1], a[2]);
+}
diff --git gcc/testsuite/g++.dg/abi/empty24.C gcc/testsuite/g++.dg/abi/empty24.C
index e69de29bb2d..81deb36ff9f 100644
--- gcc/testsuite/g++.dg/abi/empty24.C
+++ gcc/testsuite/g++.dg/abi/empty24.C
@@ -0,0 +1,25 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-Wabi=11" }
+
+struct S
+{
+ struct { } a;
+ __extension__ int b[];
+};
+
+struct S s;
+struct S a[5];
+
+void
+foo (struct S, struct S *arg1, struct S)
+{
+ if (arg1 != &a[1])
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ foo (s, &a[1], a[2]);
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-1.C gcc/testsuite/g++.dg/abi/pr60336-1.C
index e69de29bb2d..59447890cec 100644
--- gcc/testsuite/g++.dg/abi/pr60336-1.C
+++ gcc/testsuite/g++.dg/abi/pr60336-1.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-10.C gcc/testsuite/g++.dg/abi/pr60336-10.C
index e69de29bb2d..960cc2307d1 100644
--- gcc/testsuite/g++.dg/abi/pr60336-10.C
+++ gcc/testsuite/g++.dg/abi/pr60336-10.C
@@ -0,0 +1,50 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0 { };
+struct dummy1 { };
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+ va_list va_arglist;
+ int i;
+ int count = 0;
+
+ if (m == 0)
+ count++;
+ va_start (va_arglist, m);
+ i = va_arg (va_arglist, int);
+ if (i == 1)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 2)
+ i = va_arg (va_arglist, int);
+ count++;
+ if (i == 3)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 4)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 5)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 6)
+ count++;
+ va_end (va_arglist);
+ if (count != 7)
+ __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+ test (a0, 0, 1, 2, 3, 4, 5, 6);
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-11.C gcc/testsuite/g++.dg/abi/pr60336-11.C
index e69de29bb2d..14cd6d0ff3d 100644
--- gcc/testsuite/g++.dg/abi/pr60336-11.C
+++ gcc/testsuite/g++.dg/abi/pr60336-11.C
@@ -0,0 +1,56 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+ void bar (void);
+};
+struct dummy1
+{
+ void foo (void);
+};
+struct dummy : dummy0, dummy1 { };
+
+void
+test (struct dummy a, int m, ...)
+{
+ va_list va_arglist;
+ int i;
+ int count = 0;
+
+ if (m == 0)
+ count++;
+ va_start (va_arglist, m);
+ i = va_arg (va_arglist, int);
+ if (i == 1)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 2)
+ i = va_arg (va_arglist, int);
+ count++;
+ if (i == 3)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 4)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 5)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 6)
+ count++;
+ va_end (va_arglist);
+ if (count != 7)
+ __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+ test (a0, 0, 1, 2, 3, 4, 5, 6);
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-12.C gcc/testsuite/g++.dg/abi/pr60336-12.C
index e69de29bb2d..09917547930 100644
--- gcc/testsuite/g++.dg/abi/pr60336-12.C
+++ gcc/testsuite/g++.dg/abi/pr60336-12.C
@@ -0,0 +1,57 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy0
+{
+};
+struct dummy1
+{
+ unsigned : 15;
+};
+struct dummy : dummy0, dummy1
+{
+};
+
+void
+test (struct dummy a, int m, ...)
+{
+ va_list va_arglist;
+ int i;
+ int count = 0;
+
+ if (m == 0)
+ count++;
+ va_start (va_arglist, m);
+ i = va_arg (va_arglist, int);
+ if (i == 1)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 2)
+ i = va_arg (va_arglist, int);
+ count++;
+ if (i == 3)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 4)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 5)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 6)
+ count++;
+ va_end (va_arglist);
+ if (count != 7)
+ __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+ test (a0, 0, 1, 2, 3, 4, 5, 6);
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-2.C gcc/testsuite/g++.dg/abi/pr60336-2.C
index e69de29bb2d..1c6c3eb8f01 100644
--- gcc/testsuite/g++.dg/abi/pr60336-2.C
+++ gcc/testsuite/g++.dg/abi/pr60336-2.C
@@ -0,0 +1,48 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2 -Wabi=11" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...) // { dg-warning "empty" }
+{
+ va_list va_arglist;
+ int i;
+ int count = 0;
+
+ if (m == 0)
+ count++;
+ va_start (va_arglist, m);
+ i = va_arg (va_arglist, int);
+ if (i == 1)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 2)
+ i = va_arg (va_arglist, int);
+ count++;
+ if (i == 3)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 4)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 5)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 6)
+ count++;
+ va_end (va_arglist);
+ if (count != 7)
+ __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+ test (a0, 0, 1, 2, 3, 4, 5, 6); // { dg-warning "empty" }
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-3.C gcc/testsuite/g++.dg/abi/pr60336-3.C
index e69de29bb2d..4157e553b6b 100644
--- gcc/testsuite/g++.dg/abi/pr60336-3.C
+++ gcc/testsuite/g++.dg/abi/pr60336-3.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=11" }
+
+struct dummy { struct { } __attribute__((aligned (4))) a[7]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+ struct dummy a0;
+ test1 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+ test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-4.C gcc/testsuite/g++.dg/abi/pr60336-4.C
index e69de29bb2d..266f67a537d 100644
--- gcc/testsuite/g++.dg/abi/pr60336-4.C
+++ gcc/testsuite/g++.dg/abi/pr60336-4.C
@@ -0,0 +1,48 @@
+// { dg-do run { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+// { dg-options "-O2 -fabi-version=11" }
+
+#include <stdarg.h>
+
+struct dummy { };
+
+void
+test (struct dummy a, int m, ...)
+{
+ va_list va_arglist;
+ int i;
+ int count = 0;
+
+ if (m == 0)
+ count++;
+ va_start (va_arglist, m);
+ i = va_arg (va_arglist, int);
+ if (i == 1)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 2)
+ i = va_arg (va_arglist, int);
+ count++;
+ if (i == 3)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 4)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 5)
+ count++;
+ i = va_arg (va_arglist, int);
+ if (i == 6)
+ count++;
+ va_end (va_arglist);
+ if (count == 7)
+ __builtin_abort ();
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+ test (a0, 0, 1, 2, 3, 4, 5, 6);
+ return 0;
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-5.C gcc/testsuite/g++.dg/abi/pr60336-5.C
index e69de29bb2d..fe838750f55 100644
--- gcc/testsuite/g++.dg/abi/pr60336-5.C
+++ gcc/testsuite/g++.dg/abi/pr60336-5.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; struct dummy j; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-6.C gcc/testsuite/g++.dg/abi/pr60336-6.C
index e69de29bb2d..6e08c8f06fa 100644
--- gcc/testsuite/g++.dg/abi/pr60336-6.C
+++ gcc/testsuite/g++.dg/abi/pr60336-6.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i1; struct dummy i2; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-7.C gcc/testsuite/g++.dg/abi/pr60336-7.C
index e69de29bb2d..3b8b8ba6f35 100644
--- gcc/testsuite/g++.dg/abi/pr60336-7.C
+++ gcc/testsuite/g++.dg/abi/pr60336-7.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i[120]; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr60336-8.C gcc/testsuite/g++.dg/abi/pr60336-8.C
index e69de29bb2d..a1ffb64ef02 100644
--- gcc/testsuite/g++.dg/abi/pr60336-8.C
+++ gcc/testsuite/g++.dg/abi/pr60336-8.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O2 -Wabi=11" }
+
+struct dummy { struct{} a[7][3]; };
+
+extern void test1 (struct dummy, ...);
+extern void (*test2) (struct dummy, ...);
+
+void
+foo ()
+{
+ struct dummy a0;
+ test1 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+ test2 (a0, 42); // { dg-warning "ABI" "" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } }
+}
diff --git gcc/testsuite/g++.dg/abi/pr60336-9.C gcc/testsuite/g++.dg/abi/pr60336-9.C
index e69de29bb2d..393f02b62f0 100644
--- gcc/testsuite/g++.dg/abi/pr60336-9.C
+++ gcc/testsuite/g++.dg/abi/pr60336-9.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct A1 {}; struct A2 {};
+struct B1 { A1 a; A2 b; }; struct B2 { A1 a; A2 b; };
+struct C1 { B1 a; B2 b; }; struct C2 { B1 a; B2 b; };
+struct D1 { C1 a; C2 b; }; struct D2 { C1 a; C2 b; };
+struct E1 { D1 a; D2 b; }; struct E2 { D1 a; D2 b; };
+struct F1 { E1 a; E2 b; }; struct F2 { E1 a; E2 b; };
+struct G1 { F1 a; F2 b; }; struct G2 { F1 a; F2 b; };
+struct H1 { G1 a; G2 b; }; struct H2 { G1 a; G2 b; };
+struct I1 { H1 a; H2 b; }; struct I2 { H1 a; H2 b; };
+struct J1 { I1 a; I2 b; }; struct J2 { I1 a; I2 b; };
+struct dummy { J1 a; J2 b; };
+
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } }
diff --git gcc/testsuite/g++.dg/abi/pr68355.C gcc/testsuite/g++.dg/abi/pr68355.C
index e69de29bb2d..1354fc497b5 100644
--- gcc/testsuite/g++.dg/abi/pr68355.C
+++ gcc/testsuite/g++.dg/abi/pr68355.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+template<typename _Tp, _Tp __v>
+struct integral_constant
+{
+ static constexpr _Tp value = __v;
+ typedef _Tp value_type;
+ typedef integral_constant<_Tp, __v> type;
+ constexpr operator value_type() const { return value; }
+};
+
+typedef integral_constant<bool, true> true_type;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+ true_type y;
+ xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx17integral_constantIbLb1EE" { target i?86-*-* x86_64-*-* } } }
diff --git gcc/tree.c gcc/tree.c
index fa6fcb1da71..a32c238de5d 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -3135,6 +3135,20 @@ array_type_nelts (const_tree type)
? max
: fold_build2 (MINUS_EXPR, TREE_TYPE (max), max, min));
}
+
+/* Return, as an INTEGER_CST node, the number of elements for TYPE
+ (which is an ARRAY_TYPE). This counts only elements of the top
+ array. */
+
+tree
+array_type_nelts_top (const_tree type)
+{
+ return fold_build2_loc (input_location,
+ PLUS_EXPR, sizetype,
+ array_type_nelts (type),
+ size_one_node);
+}
+
/* If arg is static -- a reference to an object in static storage -- then
return the object. This is not the same as the C meaning of `static'.
@@ -13811,6 +13825,89 @@ get_nonnull_args (const_tree fntype)
return argmap;
}
+/* Returns true if TYPE is a type where it and all of its subobjects
+ (recursively) are of structure, union, or array type. */
+
+static bool
+is_empty_type (tree type)
+{
+ if (RECORD_OR_UNION_TYPE_P (type))
+ {
+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ tree ftype = TREE_TYPE (field);
+ /* Don't consider struct S { struct { } a; int b[0]; };
+ an empty type. */
+ if (TREE_CODE (ftype) == ARRAY_TYPE
+ && integer_zerop (array_type_nelts_top (ftype)))
+ {
+ tree t = DECL_CHAIN (field);
+ bool found = false;
+ /* See if the zero-length array is followed by another
+ FIELD_DECL. */
+ while (t)
+ {
+ if (TREE_CODE (t) == FIELD_DECL)
+ {
+ found = true;
+ break;
+ }
+ t = DECL_CHAIN (t);
+ }
+ if (!found)
+ return false;
+ }
+ if ((DECL_NAME (field) || RECORD_OR_UNION_TYPE_P (ftype))
+ && !is_empty_type (ftype))
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ return (integer_zerop (array_type_nelts_top (type))
+ || is_empty_type (TREE_TYPE (type)));
+ return false;
+}
+
+/* Implement TARGET_EMPTY_RECORD_P. Return true if TYPE is an empty type
+ that shouldn't be passed via stack. */
+
+bool
+is_empty_record_p (const_tree type)
+{
+ if (!abi_version_at_least (12))
+ return false;
+
+ if (type == error_mark_node)
+ return false;
+
+ if (TREE_ADDRESSABLE (type))
+ return false;
+
+ return is_empty_type (TYPE_MAIN_VARIANT (type));
+}
+
+/* Like int_size_in_bytes, but handle empty records specially. */
+
+HOST_WIDE_INT
+int_maybe_empty_type_size (const_tree type)
+{
+ return (targetm.calls.empty_record_p (type)
+ ? 0 : int_size_in_bytes (type));
+}
+
+/* Like size_in_bytes, but handle empty records specially. */
+
+tree
+maybe_empty_type_size (const_tree type)
+{
+ return (targetm.calls.empty_record_p (type)
+ ? size_zero_node : size_in_bytes (type));
+}
+
/* List of pointer types used to declare builtins before we have seen their
real declaration.
diff --git gcc/tree.h gcc/tree.h
index 7214ae2275c..fb8af8bac7d 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -4099,6 +4099,7 @@ extern tree build_method_type (tree, tree);
extern tree build_offset_type (tree, tree);
extern tree build_complex_type (tree, bool named = false);
extern tree array_type_nelts (const_tree);
+extern tree array_type_nelts_top (const_tree);
extern tree value_member (tree, tree);
extern tree purpose_member (const_tree, tree);
@@ -5434,6 +5435,9 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
extern bool nonnull_arg_p (const_tree);
extern bool is_redundant_typedef (const_tree);
+extern bool is_empty_record_p (const_tree);
+extern HOST_WIDE_INT int_maybe_empty_type_size (const_tree);
+extern tree maybe_empty_type_size (const_tree);
extern location_t
set_source_range (tree expr, location_t start, location_t finish);
Marek
More information about the Gcc-patches
mailing list