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
