One of the effects of: https://gcc.gnu.org/ml/gcc-patches/2017-10/msg02066.html
is that we now emit: (set (hard-frame-pointer) (stack-pointer)) instead of the previous non-canonical: (set (hard-frame-pointer) (plus (stack-pointer) (const_int 0))) However, recent changes to aarch64_expand_prologue mean that we can emit a frame record even if !frame_pointer_needed. In that case, the frame pointer is initialised normally, but all references generated by GCC continue to use the stack pointer. The combination of these two things triggered a regression in gcc.c-torture/execute/960419-2.c. The problem is that alias.c fundemantally requires GCC to be consistent about which register it uses: 2. stack_pointer_rtx, frame_pointer_rtx, hard_frame_pointer_rtx (if distinct from frame_pointer_rtx) and arg_pointer_rtx. Each of these rtxes has a separate ADDRESS associated with it, each with a negative id. GCC is (and is required to be) precise in which register it chooses to access a particular region of stack. We can therefore assume that accesses based on one of these rtxes do not alias accesses based on another of these rtxes. But in the test case cselib saw the move above and recorded the two registers as being equivalent. We therefore replaced some (but not all) references to the stack pointer with references to the frame pointer. MEMs that were still based on the stack pointer would then not alias MEMs that became based on the frame pointer. cselib.c has code to try to prevent this: for (; p; p = p->next) { /* Return these right away to avoid returning stack pointer based expressions for frame pointer and vice versa, which is something that would confuse DSE. See the comment in cselib_expand_value_rtx_1 for more details. */ if (REG_P (p->loc) && (REGNO (p->loc) == STACK_POINTER_REGNUM || REGNO (p->loc) == FRAME_POINTER_REGNUM || REGNO (p->loc) == HARD_FRAME_POINTER_REGNUM || REGNO (p->loc) == cfa_base_preserved_regno)) return p->loc; which refers to: /* The only thing that we are not willing to do (this is requirement of dse and if others potential uses need this function we should add a parm to control it) is that we will not substitute the STACK_POINTER_REGNUM, FRAME_POINTER or the HARD_FRAME_POINTER. These expansions confuses the code that notices that stores into the frame go dead at the end of the function and that the frame is not effected by calls to subroutines. If you allow the STACK_POINTER_REGNUM substitution, then dse will think that parameter pushing also goes dead which is wrong. If you allow the FRAME_POINTER or the HARD_FRAME_POINTER then you lose the opportunity to make the frame assumptions. */ But that doesn't work if the two registers are equal, and thus in the same value chain. It becomes pot luck which one is chosen. I think it'd be better not to enter an equivalence for the set of the frame pointer at all. That should deal with the same correctness concern as the code above, but I think we still want the existing checks for optimisation reasons. It seems better to check for pointer equality instead of register number equality though, since we don't want to change the behaviour when the frame pointer register is not being used as a frame pointer. Tested on aarch64-linux-gnu, x86_64-linux-gnu and powerpc64le-linux-gnu. Does it look like the right approach? OK to install if so? Thanks, Richard 2017-11-22 Richard Sandiford <richard.sandif...@linaro.org> gcc/ * cselib.c (expand_loc, cselib_expand_value_rtx_1): Change justification for checking for the stack and hard frame pointers. Check them by pointer rather than register number. (cselib_record_set): Do nothing for sets of the frame pointer. Index: gcc/cselib.c =================================================================== *** gcc/cselib.c 2017-11-21 17:57:59.245162437 +0000 --- gcc/cselib.c 2017-11-22 17:43:08.894805286 +0000 *************** expand_loc (struct elt_loc_list *p, stru *** 1420,1433 **** for (; p; p = p->next) { ! /* Return these right away to avoid returning stack pointer based ! expressions for frame pointer and vice versa, which is something ! that would confuse DSE. See the comment in cselib_expand_value_rtx_1 ! for more details. */ if (REG_P (p->loc) ! && (REGNO (p->loc) == STACK_POINTER_REGNUM ! || REGNO (p->loc) == FRAME_POINTER_REGNUM ! || REGNO (p->loc) == HARD_FRAME_POINTER_REGNUM || REGNO (p->loc) == cfa_base_preserved_regno)) return p->loc; /* Avoid infinite recursion trying to expand a reg into a --- 1420,1435 ---- for (; p; p = p->next) { ! /* Prefer to use references to invariant registers like the stack ! and hard frame pointers, since they are usually easier to analyze. ! E.g. alias.c can usually provide better disambiguation if given ! an address based on the stack pointer instead of one based on ! an arbitrary hard register that is set multiple times in the ! function. */ if (REG_P (p->loc) ! && (p->loc == stack_pointer_rtx ! || p->loc == frame_pointer_rtx ! || p->loc == hard_frame_pointer_rtx || REGNO (p->loc) == cfa_base_preserved_regno)) return p->loc; /* Avoid infinite recursion trying to expand a reg into a *************** cselib_expand_value_rtx_1 (rtx orig, str *** 1594,1618 **** rtx result; unsigned regno = REGNO (orig); ! /* The only thing that we are not willing to do (this ! is requirement of dse and if others potential uses ! need this function we should add a parm to control ! it) is that we will not substitute the ! STACK_POINTER_REGNUM, FRAME_POINTER or the ! HARD_FRAME_POINTER. ! ! These expansions confuses the code that notices that ! stores into the frame go dead at the end of the ! function and that the frame is not effected by calls ! to subroutines. If you allow the ! STACK_POINTER_REGNUM substitution, then dse will ! think that parameter pushing also goes dead which is ! wrong. If you allow the FRAME_POINTER or the ! HARD_FRAME_POINTER then you lose the opportunity to ! make the frame assumptions. */ ! if (regno == STACK_POINTER_REGNUM ! || regno == FRAME_POINTER_REGNUM ! || regno == HARD_FRAME_POINTER_REGNUM || regno == cfa_base_preserved_regno) return orig; --- 1596,1610 ---- rtx result; unsigned regno = REGNO (orig); ! /* Prefer to keep references to invariant registers like ! the stack and hard frame pointer. Replacing them with ! references to non-invariant registers would pessimise ! the information provided by alias.c, at least if the ! non-invariant register is set more than once in the ! function. */ ! if (orig == stack_pointer_rtx ! || orig == frame_pointer_rtx ! || orig == hard_frame_pointer_rtx || regno == cfa_base_preserved_regno) return orig; *************** cselib_record_set (rtx dest, cselib_val *** 2368,2373 **** --- 2360,2374 ---- if (REG_P (dest)) { + /* Do not record equivalences for the frame pointer, since that is + ultimately set from the stack pointer. We need to maintain + the invariant (relied on by alias.c) that references to a given + region of the stack consistently use the frame pointer or + consistently use the stack pointer; we cannot mix the two. */ + if (dest == hard_frame_pointer_rtx + || dest == frame_pointer_rtx) + return; + unsigned int dreg = REGNO (dest); if (dreg < FIRST_PSEUDO_REGISTER) {