https://gcc.gnu.org/g:16cbe70b95227a119fbd146f2947f5e162bbdbd4

commit r16-7378-g16cbe70b95227a119fbd146f2947f5e162bbdbd4
Author: Stefan Schulze Frielinghaus <[email protected]>
Date:   Sat Feb 7 10:21:02 2026 +0100

    cse: Take single register constraints into account
    
    This fixes
    
    t.c:17:3: error: 'asm' operand has impossible constraints or there are not 
enough registers
       17 |   __asm__ ("" : "=f" (a), "={fr2}" (e) : "{fr1}" (d));
          |   ^~~~~~~
    
    on powerpc64le-unknown-linux-gnu for the attached test.  Prior cse1 we have
    
    (insn 2 4 3 2 (set (reg/v:TF 120 [ c ])
            (reg:TF 33 1 [ c ])) "t.c":14:26 614 {*movtf_64bit_dm}
         (nil))
    ...
    (insn 10 9 6 2 (parallel [
                (set (reg:DF 121)
                    (asm_operands:DF ("") ("=f") 0 [
                            (reg:TF 124)
                        ]
                         [
                            (asm_input:TF ("{fr1}") t.c:17)
                        ]
                         [] t.c:17))
                (set (reg:DF 123 [ e ])
                    (asm_operands:DF ("") ("={fr2}") 1 [
                            (reg:TF 124)
                        ]
                         [
                            (asm_input:TF ("{fr1}") t.c:17)
                        ]
                         [] t.c:17))
                (clobber (reg:SI 98 ca))
            ]) "t.c":17:3 -1
         (nil))
    ...
    (insn 12 11 13 2 (set (reg:TF 33 1)
                (reg/v:TF 120 [ c ])) "t.c":18:12 614 {*movtf_64bit_dm}
         (nil))
    
    During cse1, in insn 12 pseudo 120 is substituted with hard register 33
    rendering the resulting insn trivial which is why the insn gets removed
    afterwards.  Since hard register 33 has a use after insn 12, the
    register is live before and after insn 10.  This leaves us with the
    non-trivial problem, during LRA, to also assign hard register 33 to
    pseudo 124 which is coming from the constraint of insn 10.  Since hard
    registers are not tracked, except for liveness, this cannot be solved by
    reloads which is why we end up with an error.  Therefore, treat single
    register constraints as clobbers of the respective hard registers.
    
    For the sake of symmetry this should also be done for constraints
    associated a single register class.  However, since we are in stage 4,
    there is no open PR, and I haven't done any extensive testing for single
    register classes, I'm skipping this for the moment.  Once we are back in
    stage 1, something along the lines could be added:
    
            else
              {
                enum reg_class cl
                  = reg_class_for_constraint (lookup_constraint (p));
                if (cl == NO_REGS)
                  continue;
                machine_mode mode = recog_data.operand_mode[nop];
                int regno = ira_class_singleton[cl][mode];
                if (regno >= 0)
                  invalidate_reg (gen_rtx_REG (mode, regno));
              }
    
    gcc/ChangeLog:
    
            * cse.cc (invalidate_from_sets_and_clobbers): Consider any hard
            register referred to by any single register constraint
            potentially being clobbered.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/powerpc/asm-hard-reg-2.c: New test.

Diff:
---
 gcc/cse.cc                                        | 30 +++++++++++++++++++++++
 gcc/testsuite/gcc.target/powerpc/asm-hard-reg-2.c |  9 +++++++
 2 files changed, 39 insertions(+)

diff --git a/gcc/cse.cc b/gcc/cse.cc
index a86fe3078195..4eaef602366c 100644
--- a/gcc/cse.cc
+++ b/gcc/cse.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "target.h"
 #include "rtl.h"
+#include "stmt.h"
 #include "tree.h"
 #include "cfghooks.h"
 #include "df.h"
@@ -6217,6 +6218,35 @@ invalidate_from_sets_and_clobbers (rtx_insn *insn)
            invalidate (SET_DEST (y), VOIDmode);
        }
     }
+
+  /* Any single register constraint may introduce a conflict, if the associated
+     hard register is live.  For example:
+
+     r100=%1
+     r101=42
+     r102=exp(r101)
+
+     If the first operand r101 of exp is constrained to hard register %1, then
+     r100 cannot be trivially substituted by %1 in the following since %1 got
+     clobbered.  Such conflicts may stem from single register classes as well
+     as hard register constraints.  Since prior RA we do not know which
+     alternative will be chosen, be conservative and consider any such hard
+     register from any alternative as a potential clobber.  */
+  extract_insn (insn);
+  for (int nop = recog_data.n_operands - 1; nop >= 0; --nop)
+    {
+      int c;
+      const char *p = recog_data.constraints[nop];
+      for (; (c = *p); p += CONSTRAINT_LEN (c, p))
+       if (c == ',')
+         ;
+       else if (c == '{')
+         {
+           int regno = decode_hard_reg_constraint (p);
+           machine_mode mode = recog_data.operand_mode[nop];
+           invalidate_reg (gen_rtx_REG (mode, regno));
+         }
+    }
 }
 
 static rtx cse_process_note (rtx);
diff --git a/gcc/testsuite/gcc.target/powerpc/asm-hard-reg-2.c 
b/gcc/testsuite/gcc.target/powerpc/asm-hard-reg-2.c
new file mode 100644
index 000000000000..c0aad292acba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/asm-hard-reg-2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+double a;
+double b (long double c) {
+  long double d = 0;
+  double e;
+  __asm__ ("" : "=f" (a), "={fr2}" (e) : "{fr1}" (d));
+  return c + c;
+}

Reply via email to