Matthew Fortune <matthew.fort...@imgtec.com> writes: > *) Dwarf debug for 64-bit values in floating point values for FPXX can't > be strictly correct for both 32-bit and 64-bit registers but opts to > describe one 64-bit register as that is what the FPXX ABI is emulating. > I have not yet checked what exactly happens in GDB when confronted with > this and 32-bit registers. This also impacts frame information described > via mips_save_reg and mips_restore_reg. Advice on this would be > appreciated.
I'm not sure what's best either. Clearly it's something that needs to be spelled out in the ABI, but I can imagine it would be dictated by what consumers like the unwinder and gdb find easiest to handle. > *) ISA_HAS_MXHC1 could be defined as true for all three O32 FP ABIs but > I left out FP32 to maintain historic behaviour. It should be safe to > Include it though. Thoughts? Sounds like the right call to me FWIW. Enabling it for FP32 is a separate change really. > *) Because GCC can be built to have mfpxx or mfp64 as the default option > the ASM_SPEC has to handle these specially such that they are not > passed in conjunction with -msingle-float. Depending on how all this > option handling pans out then this may also need to account for > msoft-float as well. It is an error to have -msoft-float and -mfp64 in > the assembler. The assembler and GCC shouldn't treat the options differently though. Either it should be OK for both or neither. > @@ -5141,7 +5141,7 @@ mips_get_arg_info (struct mips_arg_info *info, const > CUMULATIVE_ARGS *cum, > || SCALAR_FLOAT_TYPE_P (type) > || VECTOR_FLOAT_TYPE_P (type)) > && (GET_MODE_CLASS (mode) == MODE_FLOAT > - || mode == V2SFmode) > + || (TARGET_PAIRED_SINGLE_FLOAT && mode == V2SFmode)) > && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE); > break; This looks odd. We shouldn't have V2SF values if there's no ISA support for them. > @@ -5636,7 +5636,7 @@ mips_return_fpr_pair (enum machine_mode mode, > { > int inc; > > - inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT); > + inc = ((TARGET_NEWABI || mips_abi == ABI_32) ? 2 : MAX_FPRS_PER_FMT); Formatting nit: no extra brackets here. > @@ -6508,13 +6508,27 @@ mips_output_64bit_xfer (char direction, unsigned int > gpreg, unsigned int fpreg) > if (TARGET_64BIT) > fprintf (asm_out_file, "\tdm%cc1\t%s,%s\n", direction, > reg_names[gpreg], reg_names[fpreg]); > - else if (TARGET_FLOAT64) > + else if (ISA_HAS_MXHC1) > { > fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction, > reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]); > fprintf (asm_out_file, "\tm%chc1\t%s,%s\n", direction, > reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg]); > } > + else if (TARGET_FLOATXX && direction == 't') > + { > + /* Use the argument save area to move via memory. */ > + fprintf (asm_out_file, "\tsw\t%s,0($sp)\n", reg_names[gpreg]); > + fprintf (asm_out_file, "\tsw\t%s,4($sp)\n", reg_names[gpreg + 1]); > + fprintf (asm_out_file, "\tldc1\t%s,0($sp)\n", reg_names[fpreg]); > + } > + else if (TARGET_FLOATXX && direction == 'f') > + { > + /* Use the argument save area to move via memory. */ > + fprintf (asm_out_file, "\tsdc1\t%s,0($sp)\n", reg_names[fpreg]); > + fprintf (asm_out_file, "\tlw\t%s,0($sp)\n", reg_names[gpreg]); > + fprintf (asm_out_file, "\tlw\t%s,4($sp)\n", reg_names[gpreg + 1]); > + } The argument save area might be in use. E.g. if an argument register gets spilled, we'll generally try to spill it to the save area rather than create a new stack slot for it. This case should always be handled via SECONDARY_MEMORY_NEEDED. > @@ -10499,7 +10544,7 @@ mips_for_each_saved_acc (HOST_WIDE_INT sp_offset, > mips_save_restore_fn fn) > static void > mips_save_reg (rtx reg, rtx mem) > { > - if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64) > + if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64 && !TARGET_FLOATXX) TARGET_FLOAT32, and elsewhere. > @@ -12202,7 +12247,8 @@ mips_secondary_reload_class (enum reg_class rclass, > return NO_REGS; > > /* Otherwise, we need to reload through an integer register. */ > - return GR_REGS; > + if (regno >= 0) > + return GR_REGS; > } > if (FP_REG_P (regno)) > return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS; Why's this change needed? Although I assume it's dead code if you tested against LRA. > @@ -12210,6 +12256,22 @@ mips_secondary_reload_class (enum reg_class rclass, > return NO_REGS; > } > > +/* Implement HARD_REGNO_CALLER_SAVE_MODE. > + Always save floating-point registers using their current mode to avoid > + using a 64-bit load/store when a 64-bit FP register only contains a 32-bit > + mode. */ > + > +enum machine_mode > +mips_hard_regno_caller_save_mode (unsigned int regno, unsigned int nregs, > + enum machine_mode mode) > +{ > + return ((FP_REG_P (regno) && (mode) != VOIDmode > + && (unsigned) hard_regno_nregs[regno][mode] == nregs > + && HARD_REGNO_MODE_OK (regno, mode)) > + ? mode > + : choose_hard_reg_mode (regno, nregs, false)); > +} I'd rather go with Chao-Ying's patch: https://gcc.gnu.org/ml/gcc/2013-01/msg00180.html if there are no known downsides. > @@ -17041,6 +17103,14 @@ mips_option_override (void) > target_flags &= ~MASK_FLOAT64; > } > > + if (mips_abi != ABI_32 && TARGET_FLOATXX) > + error ("%<-mfpxx%> can only be used with the o32 ABI"); > + else if (ISA_MIPS1 > + && (TARGET_FLOATXX > + || TARGET_FLOAT64)) > + error ("%<-march=%s%> requires %<-mfp32%>", > + mips_arch_info->name); !TARGET_FLOAT32. No need for the line split. > @@ -1737,6 +1753,15 @@ struct mips_cpu_info { > > #define MODES_TIEABLE_P mips_modes_tieable_p > > +/* Odd numbered single precision registers are not considered call saved > + for O32 FPXX as they will be clobbered when run on an FR=1 FPU. */ Nit: Odd-numbered, single-precision > +#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) \ > + (TARGET_FLOATXX && ((MODE) == SFmode || (MODE) == SImode) \ > + && FP_REG_P (REGNO) && (REGNO & 1)) hard_regno_nregs[REGNO][MODE] == 1 or GET_MODE_SIZE (MODE) <= 4 might be better than explicit mode checks. > @@ -2069,6 +2094,16 @@ enum reg_class > #define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) > \ > mips_secondary_reload_class (CLASS, MODE, X, false) > > +/* When targetting the O32 FPXX ABI then all doubleword or greater moves > + to/from FP registers must be performed by FR mode aware instructions. FR-mode-aware > diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md > index f914ab6..9acc565 100644 > --- a/gcc/config/mips/mips.md > +++ b/gcc/config/mips/mips.md > @@ -431,9 +431,12 @@ > (const_string "none")) > > (define_attr "enabled" "no,yes" > - (if_then_else (ior (eq_attr "compression" "all,none") > - (and (eq_attr "compression" "micromips") > - (match_test "TARGET_MICROMIPS"))) > + (if_then_else (and (not (and (eq_attr "move_type" "mtc,mfc") > + (match_test "TARGET_FLOATXX && !ISA_HAS_MXHC1") > + (eq_attr "dword_mode" "yes"))) > + (ior (eq_attr "compression" "all,none") > + (and (eq_attr "compression" "micromips") > + (match_test "TARGET_MICROMIPS")))) > (const_string "yes") > (const_string "no"))) Probably time for this to become a (cond [...]). Took me a couple of goes to read it properly :-) > diff --git a/gcc/testsuite/gcc.target/mips/call-clobbered-2.c > b/gcc/testsuite/gcc.target/mips/call-clobbered-2.c > new file mode 100644 > index 0000000..f47cdda > --- /dev/null > +++ b/gcc/testsuite/gcc.target/mips/call-clobbered-2.c > @@ -0,0 +1,21 @@ > +/* Check that we handle call-clobbered FPRs correctly. */ > +/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */ > +/* { dg-options "-mabi=32 -modd-spreg -mfp32 -ffixed-f0 -ffixed-f1 > -ffixed-f2 -ffixed-f3 -ffixed-f4 -ffixed-f5 -ffixed-f6 -ffixed-f7 -ffixed-f8 > -ffixed-f9 -ffixed-f10 -ffixed-f11 -ffixed-f12 -ffixed-f13 -ffixed-f14 > -ffixed-f15 -ffixed-f16 -ffixed-f17 -ffixed-f18 -ffixed-f19 -ffixed-f20 > -ffixed-f22 -ffixed-f24 -ffixed-f26 -ffixed-f28 -ffixed-f30" } */ > + > +void bar (void); > +float a; > +float > +foo () > +{ > + float b = a + 1.0f; > + bar(); > + return b; > +} > +/* { dg-final { scan-assembler-times "lwc1" 2 } } */ > +/* { dg-final { scan-assembler-not "swc1" } } */ > +/* { dg-final { scan-assembler-times "sdc1" 2 } } */ > +/* { dg-final { scan-assembler-not "mtc" } } */ > +/* { dg-final { scan-assembler-not "mfc" } } */ > +/* { dg-final { scan-assembler-not "mthc" } } */ > +/* { dg-final { scan-assembler-not "mfhc" } } */ Why require sdc1 here? Would Chao-Ying's patch make this use SWC1 and LWC1 exclusively? > diff --git a/gcc/testsuite/gcc.target/mips/call-clobbered-3.c > b/gcc/testsuite/gcc.target/mips/call-clobbered-3.c > new file mode 100644 > index 0000000..3b30ce6 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/mips/call-clobbered-3.c > @@ -0,0 +1,21 @@ > +/* Check that we handle call-clobbered FPRs correctly. */ > +/* { dg-skip-if "code quality test" { *-*-* } { "-O0" "-Os" } { "" } } */ > +/* { dg-options "-mabi=32 -modd-spreg -mfpxx -ffixed-f0 -ffixed-f1 > -ffixed-f2 -ffixed-f3 -ffixed-f4 -ffixed-f5 -ffixed-f6 -ffixed-f7 -ffixed-f8 > -ffixed-f9 -ffixed-f10 -ffixed-f11 -ffixed-f12 -ffixed-f13 -ffixed-f14 > -ffixed-f15 -ffixed-f16 -ffixed-f17 -ffixed-f18 -ffixed-f19 -ffixed-f20 > -ffixed-f22 -ffixed-f24 -ffixed-f26 -ffixed-f28 -ffixed-f30" } */ > + > +void bar (void); > +float a; > +float > +foo () > +{ > + float b = a + 1.0f; > + bar(); > + return b; > +} > +/* { dg-final { scan-assembler-times "lwc1" 3 } } */ > +/* { dg-final { scan-assembler-times "swc1" 1 } } */ > +/* { dg-final { scan-assembler-times "sdc1" 2 } } */ > +/* { dg-final { scan-assembler-not "mtc" } } */ > +/* { dg-final { scan-assembler-not "mfc" } } */ > +/* { dg-final { scan-assembler-not "mthc" } } */ > +/* { dg-final { scan-assembler-not "mfhc" } } */ Does there need to be an ldc1 as well? Please explain the skips in more detail. "code quality test" only applies for -O0 skips. Anything skipped on -Os or restricted to -Os should be justified. > diff --git a/gcc/testsuite/gcc.target/mips/call-clobbered-4.c > b/gcc/testsuite/gcc.target/mips/call-clobbered-4.c > new file mode 100644 > index 0000000..872fd5e > --- /dev/null > +++ b/gcc/testsuite/gcc.target/mips/call-clobbered-4.c > @@ -0,0 +1,20 @@ > +/* Check that we handle call-clobbered FPRs correctly. */ > +/* { dg-skip-if "code quality test" { *-*-* } { "*" } { "-Os" } } */ > +/* { dg-options "-mabi=32 -modd-spreg -mfpxx -ffixed-f0 -ffixed-f1 > -ffixed-f2 -ffixed-f3 -ffixed-f4 -ffixed-f5 -ffixed-f6 -ffixed-f7 -ffixed-f8 > -ffixed-f9 -ffixed-f10 -ffixed-f11 -ffixed-f12 -ffixed-f13 -ffixed-f14 > -ffixed-f15 -ffixed-f16 -ffixed-f17 -ffixed-f18 -ffixed-f19 -ffixed-f20 > -ffixed-f22 -ffixed-f24 -ffixed-f26 -ffixed-f28 -ffixed-f30" } */ > + > +void bar (void); > +float a; > +float > +foo () > +{ > + float b = a + 1.0f; > + bar(); > + return b; > +} > +/* { dg-final { scan-assembler-times "lwc1" 2 } } */ > +/* { dg-final { scan-assembler-times "sdc1" 2 } } */ > +/* { dg-final { scan-assembler-times "mtc" 1 } } */ > +/* { dg-final { scan-assembler-times "mfc" 1 } } */ > +/* { dg-final { scan-assembler-not "mthc" } } */ > +/* { dg-final { scan-assembler-not "mfhc" } } */ What's the difference between this and call-clobbered-3.c? > @@ -248,6 +248,15 @@ set mips_option_groups { > dump "-fdump-.*" > } > > +foreach option { > + 0 1 2 3 4 5 6 7 8 9 > + 10 11 12 13 14 15 16 17 18 19 > + 20 21 22 23 24 25 26 27 28 29 > + 30 31 > +} { for { set option 0 } { $option < 32 } { incr option } { > @@ -1083,7 +1096,8 @@ proc mips-dg-options { args } { > # (*) needed by both -mbranch-likely and -mfix-r10000 > } elseif { $isa < 2 > && ([mips_have_test_option_p options "-mbranch-likely"] > - || [mips_have_test_option_p options "-mfix-r10000"]) } { > + || [mips_have_test_option_p options "-mfix-r10000"] > + || ($gp_size == 32 && [mips_have_test_option_p options > "-mfpxx"])) } { Long line > diff --git a/gcc/testsuite/gcc.target/mips/movdf-1.c > b/gcc/testsuite/gcc.target/mips/movdf-1.c > new file mode 100644 > index 0000000..d08b616 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/mips/movdf-1.c > @@ -0,0 +1,13 @@ > +/* Check that we move DFmode values via memory between FP and GP. */ > +/* { dg-options "-mabi=32 -mfpxx isa=2" } */ > + > +void bar (void); > + > +double > +foo (int x, double a) > +{ > + return a; > +} > +/* { dg-final { scan-assembler-not "mthc1" } } */ > +/* { dg-final { scan-assembler-not "mtc1" } } */ Probably worth adding some positive tests too, to make sure that the load happens with ldc1, etc. > +#elif __mips_fpr == 0 > +#define MOVE_DF_BYTE0t sw $4, 0($29); sw $5, 4($29); ldc1 $f12, 0($29) > +#define MOVE_DF_BYTE0f sdc1 $f12, 0($29); lw $4, 0($29); lw $5, 4($29) > +#define MOVE_DF_BYTE0(D) MOVE_DF_BYTE0##D > +#define MOVE_DF_BYTE8t sw $6, 8($29); sw $7, 12($29); ldc1 $f14, 8($29) > +#define MOVE_DF_BYTE8f sdc1 $f14, 8($29); lw $6, 8($29); lw $7, 12($29) > +#define MOVE_DF_BYTE8(D) MOVE_DF_BYTE8##D > +#define MOVE_DF_RETt(T) sw $2, 0($29); sw $3, 4($29); DELAYt (T, ldc1 $f0, > 0($29)) > +#define MOVE_DF_RETf(T) sdc1 $f0, 0($29); lw $2, 0($29); DELAYf (T, lw $3, > 4($29)) > +#define MOVE_DF_RET(D, T) MOVE_DF_RET##D(T) > +#define MOVE_DC_RETt(T) sw $4, 8($29); sw $5, 12($29); ldc1 $f2, 8($29); > MOVE_DF_RETt(T) > +#define MOVE_DC_RETf(T) sdc1 $f2, 8($29); lw $4, 8($29); lw $5, 12($29); > MOVE_DF_RETf(T) > +#define MOVE_DC_RET(D, T) MOVE_DF_RET##D(T) > #elif defined(__MIPSEB__) > /* FPRs are little-endian. */ > #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f13; m##D##c1 $5,$f12 Is this always safe? CALL_STUB_RET in particular uses a special ABI rather than the standard one, so I don't think we can assume that it's safe to clobber the parameter save area. Thanks, Richard