Extend this attribute to cover long double ABIs, for 64-bit too. The idea is that the linker should warn if you are linking object files with incompatible ABIs, for example IEEE128 long double with IBM long double. It's only a warning, and doesn't catch everything. In particular, global data mismatches. ie. initialised global variables in one format can be used by code that expects a different format without warning. Also, a function returning "sizeof (long double)" or similar won't cause an object file to be tagged. Oh, and you need a new linker to see long double warnings.
The patch also corrects an error that crept in to code setting rs6000_passes_float. See the added comment. Passing IEEE128 values in vsx regs ought to set both Tag_GNU_Power_ABI_FP and Tag_GNU_Power_ABI_Vector. I've also added a new option, default on, that disables output of .gnu_attribute tags. That's needed because this patch alone introduces libstdc++ testsuite regressions, fixed by the next patch. Bootstrapped and regression tested powerpc64le-linux and powerpc64-linux biarch. OK to apply? * config/rs6000/sysv4.opt (mgnu-attr): New option. * config/rs6000/rs6000.c (HAVE_LD_PPC_GNU_ATTR): Define. (rs6000_passes_float): Comment. (rs6000_passes_long_double): New static var. (call_ABI_of_interest): Return false unless rs6000_gnu_attr is set. (init_cumulative_args): Set up to emit fp .gnu.attribute for ELF 64-bit ABIs as well as 32-bit ELF. Correct rs6000_passes_float to include fp values returned in vectors. Set rs6000_passes_long_double. (rs6000_function_arg_advance_1): Likewise for function args. (rs6000_elf_file_end): Emit fp .gnu.attribute for ELF 64-bit ABIs, and SPE. Emit long double tag value too. (rs6000_opt_vars): Add gnu-attr. * configure.ac (HAVE_LD_PPC_GNU_ATTR): New ppc32 test. * configure: Regenerate. diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 4d3706c..49f662a 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -183,8 +183,16 @@ unsigned rs6000_pmode; unsigned rs6000_pointer_size; #ifdef HAVE_AS_GNU_ATTRIBUTE -/* Flag whether floating point values have been passed/returned. */ +# ifndef HAVE_LD_PPC_GNU_ATTR +# define HAVE_LD_PPC_GNU_ATTR 0 +# endif +/* Flag whether floating point values have been passed/returned. + Note that this doesn't say whether fprs are used, since the + Tag_GNU_Power_ABI_FP .gnu.attributes value this flag controls + should be set for soft-float values passed in gprs and ieee128 + values passed in vsx registers. */ static bool rs6000_passes_float; +static bool rs6000_passes_long_double; /* Flag whether vector values have been passed/returned. */ static bool rs6000_passes_vector; /* Flag whether small (<= 8 byte) structures have been returned. */ @@ -10920,7 +10928,7 @@ rs6000_return_in_msb (const_tree valtype) static bool call_ABI_of_interest (tree fndecl) { - if (symtab->state == EXPANSION) + if (rs6000_gnu_attr && symtab->state == EXPANSION) { struct cgraph_node *c_node; @@ -10997,7 +11005,7 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, } #ifdef HAVE_AS_GNU_ATTRIBUTE - if (DEFAULT_ABI == ABI_V4) + if (TARGET_ELF && (TARGET_64BIT || DEFAULT_ABI == ABI_V4)) { cum->escapes = call_ABI_of_interest (fndecl); if (cum->escapes) @@ -11025,10 +11033,19 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, <= 8)) rs6000_returns_struct = true; } - if (SCALAR_FLOAT_MODE_NOT_VECTOR_P (return_mode)) - rs6000_passes_float = true; - else if (ALTIVEC_OR_VSX_VECTOR_MODE (return_mode) - || SPE_VECTOR_MODE (return_mode)) + if (SCALAR_FLOAT_MODE_P (return_mode)) + { + rs6000_passes_float = true; + if ((HAVE_LD_PPC_GNU_ATTR || TARGET_64BIT) + && (FLOAT128_IBM_P (return_mode) + || FLOAT128_IEEE_P (return_mode) + || (return_type != NULL + && (TYPE_MAIN_VARIANT (return_type) + == long_double_type_node)))) + rs6000_passes_long_double = true; + } + if (ALTIVEC_OR_VSX_VECTOR_MODE (return_mode) + || SPE_VECTOR_MODE (return_mode)) rs6000_passes_vector = true; } } @@ -11475,16 +11492,23 @@ rs6000_function_arg_advance_1 (CUMULATIVE_ARGS *cum, machine_mode mode, cum->nargs_prototype--; #ifdef HAVE_AS_GNU_ATTRIBUTE - if (DEFAULT_ABI == ABI_V4 + if (TARGET_ELF && (TARGET_64BIT || DEFAULT_ABI == ABI_V4) && cum->escapes) { - if (SCALAR_FLOAT_MODE_NOT_VECTOR_P (mode)) - rs6000_passes_float = true; - else if (named && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) - rs6000_passes_vector = true; - else if (SPE_VECTOR_MODE (mode) - && !cum->stdarg - && cum->sysv_gregno <= GP_ARG_MAX_REG) + if (SCALAR_FLOAT_MODE_P (mode)) + { + rs6000_passes_float = true; + if ((HAVE_LD_PPC_GNU_ATTR || TARGET_64BIT) + && (FLOAT128_IBM_P (mode) + || FLOAT128_IEEE_P (mode) + || (type != NULL + && TYPE_MAIN_VARIANT (type) == long_double_type_node))) + rs6000_passes_long_double = true; + } + if ((named && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) + || (SPE_VECTOR_MODE (mode) + && !cum->stdarg + && cum->sysv_gregno <= GP_ARG_MAX_REG)) rs6000_passes_vector = true; } #endif @@ -34292,13 +34316,21 @@ static void rs6000_elf_file_end (void) { #ifdef HAVE_AS_GNU_ATTRIBUTE + /* ??? The value emitted depends on options active at file end. + Assume anyone using #pragma or attributes that might change + options knows what they are doing. */ + if ((TARGET_64BIT || DEFAULT_ABI == ABI_V4) + && rs6000_passes_float) + fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n", + ((TARGET_DF_FPR | TARGET_DF_SPE ? 1 + : TARGET_SF_FPR | TARGET_SF_SPE ? 3 + : 2) + | (!rs6000_passes_long_double ? 0 + : !TARGET_LONG_DOUBLE_128 ? 2 * 4 + : TARGET_IEEEQUAD ? 3 * 4 + : 1 * 4))); if (TARGET_32BIT && DEFAULT_ABI == ABI_V4) { - if (rs6000_passes_float) - fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n", - ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) ? 1 - : (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT) ? 3 - : 2)); if (rs6000_passes_vector) fprintf (asm_out_file, "\t.gnu_attribute 8, %d\n", (TARGET_ALTIVEC_ABI ? 2 @@ -37085,6 +37117,9 @@ static struct rs6000_opt_var const rs6000_opt_vars[] = { "warn-cell-microcode", offsetof (struct gcc_options, x_rs6000_warn_cell_microcode), offsetof (struct cl_target_option, x_rs6000_warn_cell_microcode), }, + { "gnu-attr", + offsetof (struct gcc_options, x_rs6000_gnu_attr), + offsetof (struct cl_target_option, x_rs6000_gnu_attr), }, }; /* Inner function to handle attribute((target("..."))) and #pragma GCC target diff --git a/gcc/config/rs6000/sysv4.opt b/gcc/config/rs6000/sysv4.opt index 581fcde..d5b27cc 100644 --- a/gcc/config/rs6000/sysv4.opt +++ b/gcc/config/rs6000/sysv4.opt @@ -155,3 +155,7 @@ Generate code to use a non-exec PLT and GOT. mbss-plt Target Report RejectNegative Var(secure_plt, 0) Save Generate code for old exec BSS PLT. + +mgnu-attr +Target Report Var(rs6000_gnu_attr) Init(-1) Save +Emit .gnu_attribute tags. diff --git a/gcc/configure.ac b/gcc/configure.ac index 534f22e..94912a0 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -5322,6 +5322,49 @@ if test "x$gcc_cv_ld_clearcap" = xyes; then fi AC_MSG_RESULT($gcc_cv_ld_clearcap) +case "$target" in + powerpc*-*-*) + case "$target" in + *le-*-linux*) + emul_name="-melf32lppc" + ;; + *) + emul_name="-melf32ppc" + ;; + esac + AC_CACHE_CHECK(linker .gnu_attribute long double support, + gcc_cv_ld_ppc_attr, + [gcc_cv_ld_ppc_attr=no + if test x"$ld_is_gold" = xyes; then + gcc_cv_ld_ppc_attr=yes + elif test $in_tree_ld = yes ; then + if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 28 -o "$gcc_cv_gld_major_version" -gt 2; then + gcc_cv_ld_ppc_attr=yes + fi + elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then + # check that merging the long double .gnu_attribute doesn't warn + cat > conftest1.s <<EOF + .gnu_attribute 4,1 +EOF + cat > conftest2.s <<EOF + .gnu_attribute 4,9 +EOF + if $gcc_cv_as -a32 -o conftest1.o conftest1.s > /dev/null 2>&1 \ + && $gcc_cv_as -a32 -o conftest2.o conftest2.s > /dev/null 2>&1 \ + && $gcc_cv_ld $emul_name -r -o conftest.o conftest1.o conftest2.o > /dev/null 2> conftest.err \ + && test ! -s conftest.err; then + gcc_cv_ld_ppc_attr=yes + fi + rm -f conftest.err conftest.o conftest1.o conftest2.o conftest1.s conftest2.s + fi + ]) + if test x$gcc_cv_ld_ppc_attr = xyes; then + AC_DEFINE(HAVE_LD_PPC_GNU_ATTR, 1, + [Define if your PowerPC linker has .gnu_attribute long double support.]) + fi + ;; +esac + case "$target:$tm_file" in powerpc64-*-freebsd* | powerpc64*-*-linux* | powerpc*-*-linux*rs6000/biarch64.h*) case "$target" in -- Alan Modra Australia Development Lab, IBM