Tested on hppa-unknown-linux-gnu and hppa64-hp-hpux11.11.  Committed
to trunk.

Dave
---

hppa: Fix various secondary reload issues with LRA

While commit 1887cd4b8fd0 fixed some reload issues on hppa, it
exposed some new issues.  For the most part, these involve the
handling of floating-point loads and stores.

The first issue was secondary reloads for SImode/SFmode puns were
not handled correctly and we got a SIGSEGV for stack growth in a
few compilations.  I looked at handling these reloads.  In the worst
case we needed to allocate a stack slot and a scratch register.  It
takes up to three insns to load the slot address and two insns to
copy the value through memory.  I decided in the end that it was
better to block cross-class type punning for modes that can reside
in a single float register.

We have to allow large integer/float mode changes on 64-bit HP-UX.
It has a software emulated long double.  We also need to allow
these mode changes after reload as pa_output_move_double uses DImode
to SImode type punning.  Otherwise, type punning is prohibitted in
the general register classes and classes that intersect the FP
registers.

By preventing cross-class type punning in pa_can_change_mode_class,
we were able to remove the code that attempted to handle secondary
reloads for type punned modes.  All that is left is code to strip
SUBREG wrappers on entry.

In the process of working on how to fix the cross-class type punning
issue, I noticed that the handling of secondary reloads for REG+D
addresses could be improved by handling integer and float reloads
separately.  Now we only need secondary reloads for out-of-range
displacements.

After this was implemented, we still had issues with symbolic and
constant loads for SFmode and DFmode.  We don't have support for
naked symbolic or constant loads.  This was fixed by adding a new
block in pa_emit_move_sequence to explicitly split out the address
calculation for these loads.  It also handles 64-bit PIC loads.
The pa_legitimate_constant_p and pa_legitimate_address_p functions
were updated to reject naked symbolic and constant pool SFmode/DFmode
addresses on 32-bit targets.

2026-10-10  John David Anglin  <[email protected]>

gcc/ChangeLog:

        * config/pa/pa.cc (pa_emit_move_sequence): Handle SFmode and DFmode
        symbolic, constant pool, or CONST memory references on both 32-bit
        and 64-bit targets by emitting proper PIC, 14-bit HIGH/LO_SUM, or
        1.x fallback move sequences using a scratch register.
        (pa_secondary_reload): Strip SUBREG early. Update hard-float memory
        displacement checks to account for 5-bit vs 14-bit constraints on
        PA 1.x vs PA 2.0. Look through MEM wrappers when evaluating symbolic
        address expressions. For symbolic address expressions under flag_pic
        or non-readonly conditions, return R1_REGS for SImode and DImode, but
        explicitly break out for SFmode and DFmode to let them safely fall
        back to GENERAL_REGS. Return GENERAL_REGS instead of NO_REGS for shifted
        and coprocessor/SAR reload fallbacks.
        (pa_can_change_mode_class): Remove equal-size mode change shortcut.
        Prevent cross-class type punning between integer and floating-point
        modes within GENERAL_REGS or R1_REGS before reload is completed.
        (pa_legitimate_constant_p): Reject out-of-range multi-word constants 
        in DImode on 32-bit targets. Reject naked symbolic or constant pool 
        SFmode and DFmode addresses unconditionally on 32-bit targets, while 
        intentionally allowing fully-formed LO_SUM expressions to preserve 
        PA 2.0 optimizations.
        (pa_legitimate_address_p): Reject naked symbolic, constant pool, or
        CONST expressions for SFmode and DFmode unconditionally on 32-bit
        targets to force a proper HIGH/LO_SUM split or reload.

diff --git a/gcc/config/pa/pa.cc b/gcc/config/pa/pa.cc
index 88a52d7c0ea..86e1ee548de 100644
--- a/gcc/config/pa/pa.cc
+++ b/gcc/config/pa/pa.cc
@@ -2229,6 +2229,49 @@ pa_emit_move_sequence (rtx *operands, machine_mode mode, 
rtx scratch_reg)
       return 1;
     }
 
+  if ((mode == SFmode || mode == DFmode)
+      && MEM_P (operand1)
+      && (scratch_reg || can_create_pseudo_p ())
+      && (CONSTANT_POOL_ADDRESS_P (XEXP (operand1, 0))
+         || GET_CODE (XEXP (operand1, 0)) == SYMBOL_REF
+         || GET_CODE (XEXP (operand1, 0)) == CONST))
+    {
+      rtx addr = XEXP (operand1, 0);
+      rtx scratch;
+
+      if (scratch_reg)
+       {
+         scratch = scratch_reg;
+         if (GET_MODE (scratch) != Pmode)
+           scratch = gen_rtx_REG (Pmode, REGNO (scratch));
+       }
+      else
+       scratch = gen_reg_rtx (Pmode);
+
+      if (flag_pic)
+       {
+         /* Handle PIC compilations.  */
+         rtx pic_reg = legitimize_pic_address (addr, Pmode, scratch);
+         operand1 = replace_equiv_address (operand1, pic_reg);
+       }
+      else if (INT14_OK_STRICT)
+       {
+         /* Non-PIC 32-bit PA 2.0 targets can use the 14-bit
+            displacement optimization.  */
+         emit_insn (gen_rtx_SET (scratch, gen_rtx_HIGH (Pmode, addr)));
+         operand1 = replace_equiv_address (operand1,
+                                           gen_rtx_LO_SUM (Pmode,
+                                                           scratch,
+                                                           addr));
+       }
+      else
+       {
+         /* 32-bit PA 1.x fallback.  */
+         emit_move_insn (scratch, addr);
+         operand1 = replace_equiv_address (operand1, scratch);
+       }
+    }
+
   /* Handle the most common case: storing into a register.  */
   if (register_operand (operand0, mode))
     {
@@ -6378,54 +6421,54 @@ pa_secondary_reload (bool in_p, rtx x, reg_class_t 
rclass_i,
 
   /* Strip the SUBREG to find the true underlying register entity.  */
   if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+
+  /* Handle the easy stuff first.  */
+  if ((rclass == GENERAL_REGS || FP_REG_CLASS_P (rclass))
+      && reg_plus_base_memory_operand (x, mode))
     {
-      rtx inner = SUBREG_REG (x);
-      machine_mode inner_mode = GET_MODE (inner);
+      /* If we are using soft-float, floating point modes live in the
+        general registers, meaning they follow the standard 14-bit
+        integer displacement rules.  */
+      bool hard_float_fp = (!TARGET_SOFT_FLOAT && FP_REG_CLASS_P (rclass));
 
-      /* Check if we are bridging a 32 or 64-bit Float/Integer Type-Pun.  */
-      if (REG_P (inner)
-         && ((mode == DImode && inner_mode == DFmode)
-             || (mode == DFmode && inner_mode == DImode)
-             || (mode == SImode && inner_mode == SFmode)
-             || (mode == SFmode && inner_mode == SImode)))
+      if (hard_float_fp)
        {
-         regno = REGNO (inner);
+         rtx offset = XEXP (XEXP (x, 0), 1);
+
+         /* PA 1.x hard-float instructions only have a 5-bit displacement.
+            PA 2.0 expands this to a wider 14-bit displacement range.  */
+         bool legal_fp_disp = TARGET_PA_20 ? INT_14_BITS (offset)
+                                           : INT_5_BITS (offset);
 
-         /* If the inner register has not been assigned or is bound
-            to a floating-point class, we may need a general register
-            scratchpad to handle the secondary reload.  */
-         if (regno >= FIRST_PSEUDO_REGISTER
-             || FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))
+         if (!legal_fp_disp)
            {
-             /* If we need to load/store into an FP register block
-                but our current instruction class is floating, force
-                a general register scratchpad.  */
-             if (FP_REG_CLASS_P (rclass))
-               {
-                 sri->icode = (in_p
-                   ? direct_optab_handler (reload_in_optab, mode)
-                   : direct_optab_handler (reload_out_optab, mode));
-                 return GENERAL_REGS;
-               }
+             /* An out-of-range displacement for a hard-float register
+                requires a general purpose register to fix the address.  */
+             sri->icode = (in_p
+               ? direct_optab_handler (reload_in_optab, mode)
+               : direct_optab_handler (reload_out_optab, mode));
+             return GENERAL_REGS;
            }
        }
+      else
+       {
+         /* For GENERAL_REGS (and soft-float FP targets), check if the
+            offset fits the standard 14-bit integer load/store range.  */
+         rtx offset = XEXP (XEXP (x, 0), 1);
 
-      /* Let normal processing handle the un-wrapped inner rtx if needed.  */
-      x = inner;
-    }
+         if (!INT_14_BITS (offset) && (mode == SImode || mode == DImode))
+           {
+             sri->icode = (in_p
+               ? direct_optab_handler (reload_in_optab, mode)
+               : direct_optab_handler (reload_out_optab, mode));
 
-  /* Handle the easy stuff first.  */
-  if ((rclass == GENERAL_REGS || FP_REG_CLASS_P (rclass))
-      && reg_plus_base_memory_operand (x, mode))
-    {
-      /* Guard this fallback check against narrow modes.  This guarantees
-        QImode/HImode will completely bypass direct_optab_handler loops. */
-      if (mode == SImode || mode == DImode)
-       sri->icode = (in_p
-         ? direct_optab_handler (reload_in_optab, mode)
-         : direct_optab_handler (reload_out_optab, mode));
-      else
-       sri->icode = CODE_FOR_nothing;
+             /* Let expander use its internal clobber */
+             return NO_REGS;
+           }
+       }
+
+      sri->icode = CODE_FOR_nothing;
       return NO_REGS;
     }
 
@@ -6476,33 +6519,40 @@ pa_secondary_reload (bool in_p, rtx x, reg_class_t 
rclass_i,
        default:
          gcc_unreachable ();
        }
-      return NO_REGS;
+      return R1_REGS;
     }
 
-  /* Secondary reloads of symbolic expressions require %r1 as a scratch
-     register when we're generating PIC code or when the operand isn't
-     readonly.  */
-  if (pa_symbolic_expression_p (x))
+  /* If we are given a MEM wrapper, look through it to extract the true
+     underlying address entity.  */
+  rtx addr = MEM_P (x) ? XEXP (x, 0) : x;
+
+  /* Secondary reloads of symbolic address expressions require %r1 as a
+     scratch register when we're generating PIC code or when the operand
+     isn't readonly.  */
+  if (pa_symbolic_expression_p (addr))
     {
-      if (GET_CODE (x) == HIGH)
-       x = XEXP (x, 0);
+      if (GET_CODE (addr) == HIGH)
+       addr = XEXP (addr, 0);
 
-      if (flag_pic || !read_only_operand (x, VOIDmode))
+      if (flag_pic || !read_only_operand (addr, VOIDmode))
        {
          switch (mode)
            {
            case E_SImode:
              sri->icode = CODE_FOR_reload_insi_r1;
-             break;
+             return R1_REGS;
 
            case E_DImode:
              sri->icode = CODE_FOR_reload_indi_r1;
+             return R1_REGS;
+
+           case E_SFmode:
+           case E_DFmode:
              break;
 
            default:
              gcc_unreachable ();
            }
-         return NO_REGS;
        }
     }
 
@@ -6533,7 +6583,7 @@ pa_secondary_reload (bool in_p, rtx x, reg_class_t 
rclass_i,
       sri->icode = (in_p
                    ? direct_optab_handler (reload_in_optab, mode)
                    : direct_optab_handler (reload_out_optab, mode));
-      return NO_REGS;
+      return GENERAL_REGS;
     }
 
   /* A SAR<->FP register copy requires an intermediate general register
@@ -6553,7 +6603,7 @@ pa_secondary_reload (bool in_p, rtx x, reg_class_t 
rclass_i,
          sri->icode = (in_p
                        ? direct_optab_handler (reload_in_optab, mode)
                        : direct_optab_handler (reload_out_optab, mode));
-         return NO_REGS;
+         return GENERAL_REGS;
        }
 
       /* Handle FP copy.  */
@@ -10572,9 +10622,6 @@ pa_can_change_mode_class (machine_mode from, 
machine_mode to,
   if (from == to)
     return true;
 
-  if (GET_MODE_SIZE (from) == GET_MODE_SIZE (to))
-    return true;
-
   /* Reject changes to/from modes with zero size.  */
   if (!GET_MODE_SIZE (from) || !GET_MODE_SIZE (to))
     return false;
@@ -10584,6 +10631,20 @@ pa_can_change_mode_class (machine_mode from, 
machine_mode to,
       || COMPLEX_MODE_P (to) || VECTOR_MODE_P (to))
     return false;
 
+  /* Prevent cross-class type punning impacting the general registers
+     before reload completes.  If the allocator is forced to use
+     GENERAL_REGS, disallow the mode change so it forces a proper
+     instruction breakdown.  */
+  if ((rclass == GENERAL_REGS || rclass == R1_REGS)
+      && !reload_completed
+      && GET_MODE_SIZE (to) <= GET_MODE_SIZE (DFmode)
+      && GET_MODE_SIZE (from) <= GET_MODE_SIZE (DFmode)
+      && ((GET_MODE_CLASS (from) == MODE_INT
+          && GET_MODE_CLASS (to) == MODE_FLOAT)
+         || (GET_MODE_CLASS (from) == MODE_FLOAT
+             && GET_MODE_CLASS (to) == MODE_INT)))
+    return false;
+
   /* There is no way to load QImode or HImode values directly from memory
      to a FP register.  SImode loads to the FP registers are not zero
      extended.  On the 64-bit target, this conflicts with the definition
@@ -10937,6 +10998,40 @@ pa_function_section (tree decl, enum node_frequency 
freq,
 static bool
 pa_legitimate_constant_p (machine_mode mode, rtx x)
 {
+  /* Fix early multi-word constant leaks on 32-bit targets.  */
+  if (!TARGET_64BIT
+      && mode == DImode
+      && CONST_INT_P (x)
+      && !INT_14_BITS (x))
+    return false;
+
+  /* Reject naked symbolic and constant pool SF and DF mode addresses
+     unconditionally on 32-bit targets.  We intentionally do not unwrap
+     LO_SUM expressions here, as those are valid address calculations
+     for 14-bit displacement optimizations on PA 2.0.  */
+  if (!TARGET_64BIT && (mode == SFmode || mode == DFmode))
+    {
+      rtx base = x;
+      if (GET_CODE (base) == PLUS)
+       {
+         /* Check both components of the addition for symbolic links.  */
+         rtx op0 = XEXP (base, 0);
+         rtx op1 = XEXP (base, 1);
+         if (GET_CODE (op0) == SYMBOL_REF || GET_CODE (op0) == LABEL_REF
+             || GET_CODE (op0) == CONST || CONSTANT_POOL_ADDRESS_P (op0)
+             || GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF
+             || GET_CODE (op1) == CONST || CONSTANT_POOL_ADDRESS_P (op1))
+           return false;
+       }
+
+      if (GET_CODE (base) == SYMBOL_REF
+         || GET_CODE (base) == LABEL_REF
+         || GET_CODE (base) == CONST
+         || CONSTANT_POOL_ADDRESS_P (base))
+       return false;
+    }
+
+  /* All non-zero float constants are illegitimate. */
   if (GET_MODE_CLASS (mode) == MODE_FLOAT && x != CONST0_RTX (mode))
     return false;
 
@@ -11140,6 +11235,19 @@ pa_legitimate_address_p (machine_mode mode, rtx x, 
bool strict, code_helper)
       return false;
     }
 
+  /* Reject symbolic and constant pool SF and DF mode addresses
+     unconditionally on 32-bit targets.  Even when 14-bit displacements
+     are active (PA 2.0), naked symbolic addresses cannot be loaded
+     directly into floating-point registers and must be forced through
+     a HIGH/LO_SUM split or reload.  */
+  if (!TARGET_64BIT
+      && (mode == SFmode || mode == DFmode)
+      && (GET_CODE (x) == SYMBOL_REF
+         || GET_CODE (x) == LABEL_REF
+         || GET_CODE (x) == CONST
+         || CONSTANT_POOL_ADDRESS_P (x)))
+    return false;
+
   if (GET_CODE (x) == CONST_INT && INT_5_BITS (x))
     return true;
 

Attachment: signature.asc
Description: PGP signature

Reply via email to