From: Stefan Schulze Frielinghaus <[email protected]>

This fixes

t.c:6:1: error: unable to find a register to spill
    6 | }
      | ^

for target avr.  In the PR we are given a patch which makes use of hard
register constraints in the machine description for divmodhi4.  Prior
combine we have for the test from the PR

(insn 7 6 8 2 (parallel [
            (set (reg:HI 46 [ _1 ])
                (div:HI (reg/v:HI 44 [ k ])
                    (reg:HI 48)))
            (set (reg:HI 47)
                (mod:HI (reg/v:HI 44 [ k ])
                    (reg:HI 48)))
            (clobber (scratch:HI))
            (clobber (scratch:QI))
        ]) "t.c":5:5 602 {divmodhi4}
     (expr_list:REG_DEAD (reg:HI 48)
        (expr_list:REG_DEAD (reg/v:HI 44 [ k ])
            (expr_list:REG_UNUSED (reg:HI 47)
                (nil)))))
(insn 8 7 9 2 (set (reg:HI 22 r22)
        (symbol_ref/f:HI ("*.LC0") [flags 0x2]  <var_decl 0x3fff7950d10 
*.LC0>)) "t.c":5:5 128 {*movhi_split}
     (nil))
(insn 9 8 10 2 (set (reg:HI 24 r24)
        (reg:HI 46 [ _1 ])) "t.c":5:5 128 {*movhi_split}
     (expr_list:REG_DEAD (reg:HI 46 [ _1 ])
        (nil)))

The patched instruction divmodhi4 constraints operand 2 (here pseudo
48) to hard register 22.  Combine merges insn 7 into 9 by crossing a
hard register assignment of register 22.

(note 7 6 8 2 NOTE_INSN_DELETED)
(insn 8 7 9 2 (set (reg:HI 22 r22)
        (symbol_ref/f:HI ("*.LC0") [flags 0x2]  <var_decl 0x3fff7950d10 
*.LC0>)) "t.c":5:5 128 {*movhi_split}
     (nil))
(insn 9 8 10 2 (parallel [
            (set (reg:HI 24 r24)
                (div:HI (reg:HI 49 [ k ])
                    (reg:HI 48)))
            (set (reg:HI 47)
                (mod:HI (reg:HI 49 [ k ])
                    (reg:HI 48)))
            (clobber (scratch:HI))
            (clobber (scratch:QI))
        ]) "t.c":5:5 602 {divmodhi4}
     (expr_list:REG_DEAD (reg:HI 48)
        (expr_list:REG_DEAD (reg:HI 49 [ k ])
            (nil))))

This leaves us with a conflict for pseudo 48 in the updated insn 9 since
register 22 is live here.

Fixed by pulling the sledge hammer and skipping any logical link if a
single register constraint is possibly involved.  Ideally we would skip
based on the fact whether there is any usage of a hard register referred
by any single register constraint between INSN and USE_INSN during
combine.

gcc/ChangeLog:

        * combine.cc (has_single_register_constraint_p): New function.
        (create_log_links): Do not build LOG_LINKs between insns
        containing single register constraints.
---
 gcc/combine.cc | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/gcc/combine.cc b/gcc/combine.cc
index 816324f4735..1ea99d1697c 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -76,6 +76,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "regs.h"
 #include "emit-rtl.h"
 #include "recog.h"
+#include "ira.h"
 #include "cgraph.h"
 #include "stor-layout.h"
 #include "cfgrtl.h"
@@ -1020,6 +1021,60 @@ can_combine_use_p (df_ref use)
   return true;
 }
 
+/* Return true if INSN has a single register constraint like a constraint
+   associated with a single register class or a hard register constraint.
+   Otherwise return false.  */
+static bool
+has_single_register_constraint_p (rtx_insn *insn)
+{
+  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 == '{')
+         return true;
+      /* For the moment ignore constraints with single register classes since
+        this would introduce missed optimizations.  Although, this literally
+        means we ICE in certain cases.  For example, on powerpc64le tests
+
+        FAIL: gcc.dg/cpp/_Pragma3.c (test for excess errors)
+        FAIL: gcc.target/powerpc/fold-vec-extract-char.p7.c 
scan-assembler-times \\mrldicl\\M|\\mrlwinm\\M 3
+        FAIL: gcc.target/powerpc/fold-vec-extract-short.p7.c 
scan-assembler-times \\mrldic\\M|\\mrlwinm\\M 3
+        FAIL: gcc.target/powerpc/fusion-p10-ldcmpi.c scan-assembler-times 
lbz_cmpldi_cr0_QI_clobber_CCUNS_zero 4
+        FAIL: gcc.target/powerpc/fusion-p10-ldcmpi.c scan-assembler-times 
lha_cmpdi_cr0_HI_clobber_CC_sign 16
+        FAIL: gcc.target/powerpc/fusion-p10-ldcmpi.c scan-assembler-times 
lhz_cmpldi_cr0_HI_clobber_CCUNS_zero 4
+        FAIL: gcc.target/powerpc/p9-splat-2.c scan-assembler-times lxvwsx 1
+        FAIL: gcc.target/powerpc/p9-splat-2.c scan-assembler-times xscvdpspn 2
+        FAIL: gcc.target/powerpc/p9-splat-2.c scan-assembler-times xxspltw 2
+        FAIL: gcc.target/powerpc/prefix-ds-dq.c scan-assembler-times \\mlha\\M 
1
+        FAIL: gcc.target/powerpc/prefix-ds-dq.c scan-assembler-times \\mlhz\\M 
1
+        FAIL: gcc.target/powerpc/vector_float.c scan-assembler-times 
\\mrldimi\\M 2
+        FAIL: gcc.target/powerpc/vsx-builtin-7.c scan-assembler-times xxpermdi 
35
+
+        would fail because constraint c is referring to the single count
+        register.  This should be fixed by not using a sledge hammer as
+        described below.  */
+#if 0
+       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)
+             return true;
+         }
+#endif
+    }
+  return false;
+}
+
 /* Fill in log links field for all insns.  */
 
 static void
@@ -1051,6 +1106,9 @@ create_log_links (void)
          /* Log links are created only once.  */
          gcc_assert (!LOG_LINKS (insn));
 
+         bool has_single_reg_cstr
+           = has_single_register_constraint_p (insn);
+
          FOR_EACH_INSN_DEF (def, insn)
             {
               unsigned int regno = DF_REF_REGNO (def);
@@ -1079,6 +1137,23 @@ create_log_links (void)
                  && asm_noperands (PATTERN (use_insn)) >= 0)
                continue;
 
+             /* Do not build LOG_LINKs between insns containing single
+                register constraints.  For example, assume that in the first
+                insn operand r100 of exp_a is constrained to hard register %5.
+
+                r101=exp_a(r100)
+                %5=...
+                r102=exp_b(r101)
+
+                Then combining the first insn into the last one creates a
+                conflict for pseudo r100 since hard register %5 is live.
+                Therefore, skip for now.  This is a sledge hammer approach.
+                Ideally we would skip based on the fact whether there is any
+                definition of a hard register used in a single register
+                constraint between INSN and USE_INSN.  */
+             if (has_single_reg_cstr)
+               continue;
+
              /* Don't add duplicate links between instructions.  */
              struct insn_link *links;
              FOR_EACH_LOG_LINK (links, use_insn)
-- 
2.52.0

Reply via email to