PR58139 shows a case where sched2 is scheduling the definition of a volatile VSX register across a call. The volatile VSX register (62) is actually made up the non-volatile FPR30 and another volatile doubleword, so it is actually partially volatile. Since FPR30 is defined by the PPC{,64} ABIs as being non-volatile, it is not marked in CALL_USED_REGISTERS[]. This means that the DF infrastruture doesn't record the dependency between the call and the VSX instruction, so sched2 freely schedules the VSX instruction past the call.
There is code in sched-deps.c:deps_analyze_insn() that looks for partial clobbers by testing HARD_REGNO_CALL_PART_CLOBBERED (when insn is a call insn). HARD_REGNO_CALL_PART_CLOBBERED is correctly handled, meaning passing in 62 and a vector mode like V2DFmode returns true. However, we don't see the clobber because the mode it uses when testing HARD_REGNO_CALL_PART_CLOBBERED is reg_raw_mode[62], which is set to DImode, and hardreg 62 is not partially clobbered in DImode. The definition of reg_raw_mode[] is defined as: /* For each hard register, the widest mode object that it can contain. This will be a MODE_INT mode if the register can hold integers. Otherwise it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the register. */ Now DImode is not the widest mode that can fit into hardreg 62, rather it is the vector modes like V2DFmode, etc. The code that initializes reg_raw_mode[] is in reginfo.c:init_reg_modes_target() which uses the value returned by choose_hard_reg_mode(i, 1, false). Looking at choose_hard_reg_mode(), it loops through several mode classes looking for modes that are ok. The bug seems to be that it returns too quickly when it finds a valid mode. To find the "widest" mode, it really should scan through all mode classes, choosing the widest mode it encounters. The patch below does that and I can confirm with this patch, the scheduler sees the dependency between the call and the vsx instruction, correctly blocking the move. Pat Haugen SPEC tested this on powerpc64-linux and saw no degradations. This also passed powerpc64-linux bootstrap and regtesting. Is this Ok for mainline? Since this is a wrong code bug, is this also ok on the 4.8 branch after a couple of weeks, as long as no one finds any issues with it? Peter PR58139 * gcc/reginfo.c (choose_hard_reg_mode): San through all mode classes looking for widest mode. Index: gcc/reginfo.c =================================================================== --- gcc/reginfo.c (revision 201779) +++ gcc/reginfo.c (working copy) @@ -620,40 +620,35 @@ choose_hard_reg_mode (unsigned int regno mode = GET_MODE_WIDER_MODE (mode)) if ((unsigned) hard_regno_nregs[regno][mode] == nregs && HARD_REGNO_MODE_OK (regno, mode) - && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))) + && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)) + && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode)) found_mode = mode; - if (found_mode != VOIDmode) - return found_mode; - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if ((unsigned) hard_regno_nregs[regno][mode] == nregs && HARD_REGNO_MODE_OK (regno, mode) - && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))) + && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)) + && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode)) found_mode = mode; - if (found_mode != VOIDmode) - return found_mode; - for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if ((unsigned) hard_regno_nregs[regno][mode] == nregs && HARD_REGNO_MODE_OK (regno, mode) - && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))) + && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)) + && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode)) found_mode = mode; - if (found_mode != VOIDmode) - return found_mode; - for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) if ((unsigned) hard_regno_nregs[regno][mode] == nregs && HARD_REGNO_MODE_OK (regno, mode) - && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))) + && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)) + && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode)) found_mode = mode; if (found_mode != VOIDmode)