This patch corrects the manner in which single element structures are passed. The implementation always has been wrong and did not match the AIX ABI. The AIX ABI specifies that aggregates are passed by values in GPRs, but GCC passes small floating point aggregates in FPRs. The PPC64 BE ELFv1 Linux ABI was updated in the past to match the behavior of GCC.
For AIX, the implementation can produce wrong code for the case of a single precision floating point value (SFmode) argument in 64 bit mode. The rs6000 backend parameter passing code chose the location for arguments based on the MODE of the argument. For small aggregates, GCC packs the aggregate into a scalar mode and the function arg machinery is presented with an argument mode of the scalar mode, as opposed to BLKmode. On AIX, this meant that a single element floating point aggregate (float or double) is represented as SFmode or DFmode. The current rs6000 function arg machinery passes the arguments in FPRs. On AIX, argument are padded upwards in the register, as if the arguments are strings, not numerical values. In 64 bit mode, GCC dutifully shifts a 32 bit SFmode value so that it is loaded into the upper 32 bits of the FPR. Power FPRs always are 64 bit double precision format, regardless of the precision, so the value loaded is garbage, in addition to being passed in the wrong location. In 32 bit mode (still the default for AIX), GCC didn't try to shift the value, so it accidentally worked -- self consistently passing the value in an FPR that GCC interpreted as word mode size. This patch adds a test for the argument TYPE to override the floating point argument passing for aggregates only for AIX. Yes, this will break the ABI for GCC on AIX. GCC was not following the AIX ABI. No one really noticed. It only produced wrong code in 64 bit mode. Single element aggregates are rare. The change sucks, but it doesn't make sense to have GCC produce code that is incompatible with the AIX ABI. I am using AGGREGATE_TYPE_P() to test the type. I believe that macro captures all GCC cases of interest. Please let me know if this seems like the wrong choice. The macro includes ARRAY_TYPE, but arrays commonly are passed by reference or pointer, not by value, so I don't believe that this will affect single element vectors, but please correct me if I'm wrong. Another option is RECORD_OR_UNION_TYPE_P(). I believe that this change will correct GCC's conformance with the AIX ABI. I realize that most people don't know enough or care enough about AIX and the ABI to determine if the conformance is correct. I am interested in any feedback about the mechanism (AGGREGATE_TYPE_P) used to select the behavior. Bootstrapped on powerpc-ibm-aix7.2.0.0 Thanks, David * config/rs6000/rs6000.c (rs6000_function_arg): Don't pass aggregates in FPRs on AIX. (rs6000_arg_partial_bytes): Same. Index: rs6000.c =================================================================== --- rs6000.c (revision 266671) +++ rs6000.c (working copy) @@ -11989,7 +11989,8 @@ rs6000_function_arg (cumulative_args_t cum_v, mach if (elt_mode == TDmode && (cum->fregno % 2) == 1) cum->fregno++; - if (USE_FP_FOR_ARG_P (cum, elt_mode)) + if (USE_FP_FOR_ARG_P (cum, elt_mode) + && !(TARGET_AIX && AGGREGATE_TYPE_P (type))) { rtx rvec[GP_ARG_NUM_REG + AGGR_ARG_NUM_REG + 1]; rtx r, off; @@ -12125,7 +12126,8 @@ rs6000_arg_partial_bytes (cumulative_args_t cum_v, align_words = rs6000_parm_start (mode, type, cum->words); - if (USE_FP_FOR_ARG_P (cum, elt_mode)) + if (USE_FP_FOR_ARG_P (cum, elt_mode) + && !(TARGET_AIX && AGGREGATE_TYPE_P (type))) { unsigned long n_fpreg = (GET_MODE_SIZE (elt_mode) + 7) >> 3;