Generally, on 32-bit machines, 64-bit integer addition is performed by
splitting the operation into its upper and lower parts.  For examole,

        dest:DI = src0:DI + src1:DI;

is tranformed into

        dest_hi:SI = src0_hi:SI + src1_hi:SI;
        dest_lo:SI = src0_lo:SI + src1_lo:SI;
        if (dest_lo:SI <U src0_lo:SI)
          ++dest_hi;

or,

        dest_hi:SI = src0_hi:SI + src1_hi:SI;
        dest_lo:SI = src0_lo:SI + src1_lo:SI;
        dest_hi:SI += (dest_lo:SI <U src0_lo:SI);

As in the example above, in machines that do not have a special mechanism
for determining carry-overs, it is necessary to explicitly determine that
the result of the lower part of the calculation is unsigned smaller than
the result before addition.

Unfortunately, the same hardware register may be assigned to those two
(dest_lo and src0_lo in the example above), and as a result, an assignment
to a scratch register may be inserted (for src0_lo).

Since addition is a commutative operation, in such cases the problem can
be simply solved by swapping the two operands of the addition.  However,
without taking the preceding addition instruction into account, this is
impossible with the comparison alone.

Fortunately, the RTL instruction combination pass provides combinations
of insns that are suitable for this type of handling, namely patterns that
handle addition and comparison of the result in parallel.

    Trying 8 -> 9:
        8: r52:SI=r48:SI+r67:SI
          REG_DEAD r67:SI
        9: pc={(geu(r52:SI,r48:SI))?L11:pc}
          REG_DEAD r48:SI
          REG_BR_PROB 536870913
    Successfully matched this instruction:
    (parallel [
            (set (pc)
                (if_then_else (geu (plus:SI (reg:SI 48 [ a ])
                            (reg:SI 67 [ b ]))
                        (reg:SI 48 [ a ]))
                    (label_ref 11)
                    (pc)))
            (set (reg:SI 52 [ _3 ])
                (plus:SI (reg:SI 48 [ a ])
                    (reg:SI 67 [ b ])))
        ])
    allowing combination of insns 8 and 9
    original costs 4 + 12 = 16
    replacement cost 16
    deferring deletion of insn with uid = 8.
    modifying insn i3     9: 
{pc={(geu(r48:SI+r67:SI,r48:SI))?L11:pc};r52:SI=r48:SI+r67:SI;}
          REG_DEAD r67:SI
          REG_BR_PROB 536870913
          REG_DEAD r48:SI
    deferring rescan insn with uid = 9.

This makes it possible to appropriately swap the two operands after the RA,
yielding the desired result.

gcc/ChangeLog:

        * config/xtensa/xtensa.cc
        (adddi3): Remove the previous sloppy workaround.
        (adddi3_lowpart_0, adddi3_lowpart_1): New insn_and_split patterns.
---
 gcc/config/xtensa/xtensa.md | 75 +++++++++++++++++++++++++++++++++----
 1 file changed, 67 insertions(+), 8 deletions(-)

diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md
index 9df4fbe9218..1bc05a8bdc7 100644
--- a/gcc/config/xtensa/xtensa.md
+++ b/gcc/config/xtensa/xtensa.md
@@ -227,10 +227,6 @@
 {
   rtx lo_dest, hi_dest, lo_op0, hi_op0, lo_op1, hi_op1;
   rtx_code_label *label;
-  if (rtx_equal_p (operands[0], operands[1])
-      || rtx_equal_p (operands[0], operands[2])
-      || ! REG_P (operands[1]) || ! REG_P (operands[2]))
-    FAIL;
   lo_dest = gen_lowpart (SImode, operands[0]);
   hi_dest = gen_highpart (SImode, operands[0]);
   lo_op0 = gen_lowpart (SImode, operands[1]);
@@ -239,15 +235,78 @@
   hi_op1 = gen_highpart (SImode, operands[2]);
   emit_insn (gen_addsi3 (hi_dest, hi_op0, hi_op1));
   emit_insn (gen_addsi3 (lo_dest, lo_op0, lo_op1));
-  emit_cmp_and_jump_insns (lo_dest,
-                          (REGNO (operands[1]) < REGNO (operands[2])
-                           ? lo_op1 : lo_op0), GEU, const0_rtx,
-                          SImode, true, label = gen_label_rtx ());
+  emit_cmp_and_jump_insns (lo_dest, lo_op0, GEU, const0_rtx, SImode,
+                          true, label = gen_label_rtx ());
   emit_insn (gen_addsi3 (hi_dest, hi_dest, const1_rtx));
   emit_label (label);
   DONE;
 })
+;; The RTL combiner can recognize two examples of insn sequence patterns
+;; that perform the lower half of a 64-bit addition, as shown below.
+;; To correctly detect an overflow, it is necessary to use a source register
+;; distinct from the destination register.  To achieve this, if the destina-
+;; tion and the first source are the same, the two sources are swapped; this
+;; is justified by the fact that the two sources of addition operation are
+;; commutative.
+
+(define_insn_and_split "*adddi3_lowpart_0"
+  [(set (pc)
+       (if_then_else (geu (plus:SI (match_operand:SI 1 "register_operand" "r")
+                                   (match_operand:SI 2 "register_operand" "r"))
+                          (match_dup 1))
+                     (label_ref (match_operand 3 ""))
+                     (pc)))
+   (set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (match_dup 1)
+                (match_dup 2)))]
+  "! rtx_equal_p (operands[1], operands[2])"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  if (rtx_equal_p (operands[0], operands[1]))
+    std::swap (operands[1], operands[2]);
+  emit_insn (gen_addsi3 (operands[0], operands[1], operands[2]));
+  emit_jump_insn (gen_cbranchsi4 (gen_rtx_GEU (VOIDmode, operands[0], 
operands[1]),
+                                 operands[0], operands[1], operands[3]));
+  DONE;
+}
+  [(set_attr "type"  "jump")
+   (set_attr "mode"  "SI")
+   (set (attr "length")
+       (if_then_else (match_test "TARGET_DENSITY")
+                     (const_int 5)
+                     (const_int 6)))])
+
+(define_insn_and_split "*adddi3_lowpart_1"
+  [(set (match_operand:SI 3 "register_operand" "=a")
+       (ltu:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
+                        (match_operand:SI 2 "register_operand" "r"))
+               (match_dup 1)))
+   (set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (match_dup 1)
+                (match_dup 2)))]
+  "TARGET_SALT && ! rtx_equal_p (operands[1], operands[2])"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  if (rtx_equal_p (operands[0], operands[1]))
+    std::swap (operands[1], operands[2]);
+  emit_insn (gen_addsi3 (operands[0], operands[1], operands[2]));
+  emit_insn (gen_cstoresi4 (operands[3],
+                           gen_rtx_LTU (VOIDmode, operands[0], operands[1]),
+                           operands[0], operands[1]));
+  DONE;
+}
+  [(set_attr "type"  "multi")
+   (set_attr "mode"  "SI")
+   (set (attr "length")
+       (if_then_else (match_test "TARGET_DENSITY")
+                     (const_int 5)
+                     (const_int 6)))])
+
 (define_insn "addsf3"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (plus:SF (match_operand:SF 1 "register_operand" "%f")
--
2.39.5

Reply via email to