[arm] Pattern match insns for a + ~b + Carry
authorRichard Earnshaw <rearnsha@arm.com>
Thu, 31 Oct 2019 16:04:53 +0000 (16:04 +0000)
committerRichard Earnshaw <rearnsha@gcc.gnu.org>
Thu, 31 Oct 2019 16:04:53 +0000 (16:04 +0000)
On ARM, the SBC instruction is defined as

  Ra - Rb - ~C

where C is the carry flag.  But -Rb = ~Rb + 1, so this is equivalent to

  Ra + ~Rb + 1 - ~C

which then simplifies to

  Ra + ~Rb + C

which is essentially an add-with-carry with one operand inverted.  We
can define RTL patterns to match this.  In thumb2 we can only match
when the operands are both registers, but in Arm state we can also use
RSC to match when Rn is either a constant or a shifted operand.

This overall simplifies some cases of 64-bit arithmetic, for example,

int64_t f (int64_t a, int64_t b) { return a + ~b; }

will now compile to

  MVN  R2, R2
  ADDS R0, R0, R2
  SBC  R1, R1, R3

* config/arm/arm.md (add_not_cin): New insn.
(add_not_shift_cin): Likewise.

From-SVN: r277676

gcc/ChangeLog
gcc/config/arm/arm.md

index 5ce04fe3f3c98dba3c2030a15f1bb79d0baf4e3e..e520cf054036f875eedc22e074a0d8ee96448fa5 100644 (file)
@@ -1,3 +1,8 @@
+2019-10-31  Richard Earnshaw  <rearnsha@arm.com>
+
+       * config/arm/arm.md (add_not_cin): New insn.
+       (add_not_shift_cin): Likewise.
+
 2019-10-31  Martin Liska  <mliska@suse.cz>
 
        * ipa-icf-gimple.c (func_checker::compare_tree_ssa_label): Remove.
index ae77cc377f62478f431b980feb7164e292d39686..4f035cbfddd574c86cd907b579ddaf93be179943 100644 (file)
    (set_attr "type" "adc_imm")]
 )
 
+;; SBC performs Rn - Rm - ~C, but -Rm = ~Rm + 1 => Rn + ~Rm + 1 - ~C
+;; => Rn + ~Rm + C, which is essentially ADC Rd, Rn, ~Rm
+(define_insn "*add_not_cin"
+  [(set (match_operand:SI 0 "s_register_operand" "=r,r")
+       (plus:SI
+        (plus:SI (not:SI (match_operand:SI 1 "s_register_operand" "r,r"))
+                 (match_operand:SI 3 "arm_carry_operation" ""))
+        (match_operand:SI 2 "arm_rhs_operand" "r,I")))]
+  "TARGET_ARM || (TARGET_THUMB2 && !CONST_INT_P (operands[2]))"
+  "@
+   sbc%?\\t%0, %2, %1
+   rsc%?\\t%0, %1, %2"
+  [(set_attr "conds" "use")
+   (set_attr "predicable" "yes")
+   (set_attr "arch" "*,a")
+   (set_attr "type" "adc_reg,adc_imm")]
+)
+
+;; On Arm we can also use the same trick when the non-inverted operand is
+;; shifted, using RSC.
+(define_insn "add_not_shift_cin"
+  [(set (match_operand:SI 0 "s_register_operand" "=r,r")
+       (plus:SI
+        (plus:SI (match_operator:SI 3 "shift_operator"
+                  [(match_operand:SI 1 "s_register_operand" "r,r")
+                   (match_operand:SI 2 "shift_amount_operand" "M,r")])
+                 (not:SI (match_operand:SI 4 "s_register_operand" "r,r")))
+        (match_operand:SI 5 "arm_carry_operation" "")))]
+  "TARGET_ARM"
+  "rsc%?\\t%0, %4, %1%S3"
+  [(set_attr "conds" "use")
+   (set_attr "predicable" "yes")
+   (set_attr "type" "alu_shift_imm,alu_shift_reg")]
+)
+
 (define_insn "cmpsi3_carryin_<CC_EXTEND>out"
   [(set (reg:<CC_EXTEND> CC_REGNUM)
        (compare:<CC_EXTEND>