From: Stefan Schulze Frielinghaus <[email protected]>

I have put the check into cant_combine_insn_p().  However, I still
haven't convinced myself that this is a full fledged solution.  Even the
current behaviour for constraints associated a single register class
seems like only partially solved to me.  For the current case it would
solve the problem because we are substituting into a reg-reg move.
However, my current understanding is that I could actually make use of
hard registers in arbitrary patterns which wouldn't be dealt with.

With this patch I'm conservative when hard register constraints are
involved and skip any combination.

In light of not having real world examples were it breaks also for
constraints associated a single register class and being in stage 4, I'm
limiting this to hard register constraints for now.

@Jeff ok for mainline assuming that bootstrap and regtest are successful
for x86_64 and s390?

@Johann if you have a larger test than what you provided in the PR,
could you give this patch a try?  I can confirm that with the machine
description patch provided in the PR, the example from the PR compiles
fine if this patch is applied.  If you still see failures in more
complex examples you might also want to apply
https://gcc.gnu.org/pipermail/gcc-patches/2026-January/705494.html
https://gcc.gnu.org/pipermail/gcc-patches/2026-January/705495.html
Note, I haven't tested hard register constraints in clobbers so far, and
would rather assume that they do not work as expected.  Once I find some
time, I will look into this.

-- 8< --

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 potential
combination if a hard register constraint is involved.  Ideally we would
skip based on the fact whether there is any usage of a hard register
referred by any hard register constraint between potentially combined
insns.

gcc/ChangeLog:

        * combine.cc (cant_combine_insn_p): Do not try to combine insns
        if hard register constraints are involved.
---
 gcc/combine.cc | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/gcc/combine.cc b/gcc/combine.cc
index 816324f4735..a4e7aff971d 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -2229,6 +2229,27 @@ cant_combine_insn_p (rtx_insn *insn)
   if (!NONDEBUG_INSN_P (insn))
     return true;
 
+  /* Do not try to combine insns which make use of hard 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 for the last insn.  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 potentially combined insns.  */
+
+  extract_insn (insn);
+  for (int nop = recog_data.n_operands - 1; nop >= 0; --nop)
+    {
+      if (strchr (recog_data.constraints[nop], '{'))
+       return true;
+    }
+
   /* Never combine loads and stores involving hard regs that are likely
      to be spilled.  The register allocator can usually handle such
      reg-reg moves by tying.  If we allow the combiner to make
-- 
2.52.0

Reply via email to