https://gcc.gnu.org/g:5c8ec67c31def7e82654e55908f8fa6541ad4bd2
commit r17-1514-g5c8ec67c31def7e82654e55908f8fa6541ad4bd2 Author: Stefan Schulze Frielinghaus <[email protected]> Date: Mon Jun 8 09:10:20 2026 +0200 lra: Reloading section anchors Currently an "entire" address is reloaded even in cases where section anchors are involved. This makes it harder to share section anchors which is the whole point of them. For example, in cases where offsetable MEMs are valid do not reload .LANCHOR42+offset but only .LANCHOR42 and replace the address with the resulting reload register and the offset. As a consequence subsequent passes only have to deal with register equivalences in order to share section anchors. For example, consider testsuite/gcc.target/s390/section-anchors-4.c. Without this patch, after LRA we end up with 20: %r1:DI=`*.LANCHOR0' 17: %f0:DF=[%r1:DI] 19: %r1:DI=const(`*.LANCHOR0'+0x8) 12: {%f0:DF=%f0:DF+[%r1:DI];clobber %cc:CC;} and with this patch 20: %r1:DI=`*.LANCHOR0' 17: %f0:DF=[%r1:DI] 19: %r1:DI=`*.LANCHOR0' 12: {%f0:DF=%f0:DF+[%r1:DI+0x8];clobber %cc:CC;} In insn 19 only the section anchor is reloaded. This allows postreload to remove the redundant load such that we end up with 20: %r1:DI=`*.LANCHOR0' 17: %f0:DF=[%r1:DI] 12: {%f0:DF=%f0:DF+[%r1:DI+0x8];clobber %cc:CC;} gcc/ChangeLog: * lra-constraints.cc (reload_section_anchor_p): New function. (curr_insn_transform): For offsetable MEMs, try reloading the section anchor only. gcc/testsuite/ChangeLog: * gcc.target/s390/section-anchors-4.c: New test. Diff: --- gcc/lra-constraints.cc | 73 +++++++++++++++++++++++ gcc/testsuite/gcc.target/s390/section-anchors-4.c | 24 ++++++++ 2 files changed, 97 insertions(+) diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc index bd45d32cc59a..51f239eea8df 100644 --- a/gcc/lra-constraints.cc +++ b/gcc/lra-constraints.cc @@ -4307,6 +4307,58 @@ postpone_insns (rtx_insn *first) } } +/* Test whether the n-th operand is a MEM where the address is the sum of a + section anchor and a constant and return true in case of reloading the + section anchor only results in a satisfiable operand w.r.t. its + corresponding constraint. Otherwise return false. */ + +static bool +reload_section_anchor_p (int nop) +{ + rtx op = *curr_id->operand_loc[nop]; + if (!MEM_P (op)) + return false; + rtx addr = XEXP (op, 0); + + if (GET_CODE (addr) != CONST + || GET_CODE (XEXP (addr, 0)) != PLUS + || GET_CODE (XEXP (XEXP (addr, 0), 0)) != SYMBOL_REF + || !SYMBOL_REF_ANCHOR_P (XEXP (XEXP (addr, 0), 0)) + || !CONST_INT_P (XEXP (XEXP (addr, 0), 1)) + /* Some offsets are valid in conjunction with a symbol and + invalid in conjunction with a register. Thus, pull out + the anchor only in case the offset is a valid anchor + offset. */ + || INTVAL (XEXP (XEXP (addr, 0), 1)) < targetm.min_anchor_offset + || INTVAL (XEXP (XEXP (addr, 0), 1)) > targetm.max_anchor_offset) + return false; + + /* Now test whether a new address of the form REG+DISPLACEMENT is valid for + the selected alternative. In order to do so, utilize lra_pmode_pseudo + instead of an actual reload register. */ + + rtx offset = XEXP (XEXP (addr, 0), 1); + rtx new_addr = gen_rtx_PLUS (Pmode, lra_pmode_pseudo, offset); + rtx new_op = shallow_copy_rtx (op); + XEXP (new_op, 0) = new_addr; + + /* Get operand constraints for given alternative. */ + const char *p = (curr_static_id->operand_alternative + [goal_alt_number * curr_static_id->n_operands + nop] + .constraint); + char c; + for (; + (c = *p) && c != ',' && c != '#'; + p += CONSTRAINT_LEN (c, p)) + { + enum constraint_num cn = lookup_constraint (p); + if (constraint_satisfied_p (new_op, cn)) + return true; + } + + return false; +} + /* Main entry point of the constraint code: search the body of the current insn to choose the best alternative. It is mimicking insn alternative cost calculation model of former reload pass. That is @@ -4874,6 +4926,27 @@ curr_insn_transform (bool check_only_p) new_reg = emit_inc (rclass, *loc, /* This value does not matter for MODIFY. */ GET_MODE_SIZE (GET_MODE (op))); + /* Try to pull out section anchors. For example, instead of + reloading an "entire" address like .LANCHOR42+offset only reload + .LANCHOR42 and use the new reload register as the base register. + This allows following optimizations to share section anchors and + remove redundant loads. */ + else if (reload_section_anchor_p (i)) + { + rtx anchor = XEXP (XEXP (*loc, 0), 0); + rtx offset = XEXP (XEXP (*loc, 0), 1); + + if (get_reload_reg (OP_IN, Pmode, anchor, rclass, NULL, false, + false, "offsetable address", &new_reg)) + lra_emit_move (new_reg, anchor); + + rtx new_addr = gen_rtx_PLUS (Pmode, new_reg, offset); + rtx new_op = shallow_copy_rtx (op); + XEXP (new_op, 0) = new_addr; + + new_reg = new_op; + loc = curr_id->operand_loc[i]; + } else if (get_reload_reg (OP_IN, Pmode, *loc, rclass, NULL, false, false, "offsetable address", &new_reg)) diff --git a/gcc/testsuite/gcc.target/s390/section-anchors-4.c b/gcc/testsuite/gcc.target/s390/section-anchors-4.c new file mode 100644 index 000000000000..9938c057437d --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/section-anchors-4.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=z13" } */ +/* { dg-final { scan-assembler-times "\tlarl\t" 1 } } */ + +/* Prior LRA we have + + 12: {%f0:DF=[`*.LANCHOR0']+[const(`*.LANCHOR0'+0x8)];clobber %cc:CC;} + + and afterwards + + 20: %r1:DI=`*.LANCHOR0' + 17: %f0:DF=[%r1:DI] + 19: %r1:DI=`*.LANCHOR0' + 12: {%f0:DF=%f0:DF+[%r1:DI+0x8];clobber %cc:CC;} + + where postreload removes the redundant insn 19. */ + +double x, y; + +double +test () +{ + return x + y; +}
