aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorRichard Henderson <rth@redhat.com>2011-01-19 10:55:12 -0800
committerRichard Henderson <rth@gcc.gnu.org>2011-01-19 10:55:12 -0800
commit442178234103c031924b131257aca4b1b1f03049 (patch)
tree84ff74222c89ca95b4efea29170a06523da5adb5 /gcc
parent37a185d79e630fd3ad5acd7ac5a29ac07a5be4d6 (diff)
downloadgcc-442178234103c031924b131257aca4b1b1f03049.zip
gcc-442178234103c031924b131257aca4b1b1f03049.tar.gz
gcc-442178234103c031924b131257aca4b1b1f03049.tar.bz2
mn10300: Implement adddi3, subdi3.
Via expander, pre- and post-reload patterns. The pre-reload pattern is defined to allow lower_subregs totally split the DImode values. From-SVN: r169014
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog6
-rw-r--r--gcc/config/mn10300/mn10300.md321
-rw-r--r--gcc/config/mn10300/predicates.md5
3 files changed, 332 insertions, 0 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 46cb383..670fb68 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,11 @@
2011-01-19 Richard Henderson <rth@redhat.com>
+ * config/mn10300/mn10300.md (addsi3_flags): New.
+ (addc_internal, adddi3, adddi3_internal, *adddi3_degenerate): New.
+ (subsi3_flags, subc_internal, subdi3): New.
+ (subdi3_internal, *subdi3_degenerate): New.
+ * config/mn10300/predicates.md (reg_or_am33_const_operand): New.
+
* config/mn10300/mn10300.c (mn10300_can_use_retf_insn): New.
(mn10300_can_use_rets_insn): Rename from mn10300_can_use_return_insn.
(mn10300_expand_epilogue): Use it. Compute REG_SAVE_BYTES once.
diff --git a/gcc/config/mn10300/mn10300.md b/gcc/config/mn10300/mn10300.md
index f3f6baf..bb95b37 100644
--- a/gcc/config/mn10300/mn10300.md
+++ b/gcc/config/mn10300/mn10300.md
@@ -543,6 +543,172 @@
[(set_attr "timings" "11,22")]
)
+;; A helper to expand the above, with the CC_MODE filled in.
+(define_expand "addsi3_flags"
+ [(parallel [(set (match_operand:SI 0 "register_operand")
+ (plus:SI (match_operand:SI 1 "register_operand")
+ (match_operand:SI 2 "nonmemory_operand")))
+ (set (reg:CCZNC CC_REG)
+ (compare:CCZNC (plus:SI (match_dup 1) (match_dup 2))
+ (const_int 0)))])]
+ ""
+)
+
+(define_insn "addc_internal"
+ [(set (match_operand:SI 0 "register_operand" "=D,r,r")
+ (plus:SI
+ (plus:SI
+ (ltu:SI (reg:CC CC_REG) (const_int 0))
+ (match_operand:SI 1 "register_operand" "%0,0,r"))
+ (match_operand:SI 2 "reg_or_am33_const_operand" " D,i,r")))
+ (clobber (reg:CC CC_REG))]
+ "reload_completed"
+ "@
+ addc %2,%0
+ addc %2,%0
+ addc %2,%1,%0"
+ [(set_attr "isa" "*,am33,am33")]
+)
+
+(define_expand "adddi3"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "nonmemory_operand" "")))]
+ ""
+{
+ rtx op0l, op0h, op1l, op1h, op2l, op2h;
+
+ op0l = gen_lowpart (SImode, operands[0]);
+ op1l = gen_lowpart (SImode, operands[1]);
+ op2l = gen_lowpart (SImode, operands[2]);
+ op0h = gen_highpart (SImode, operands[0]);
+ op1h = gen_highpart (SImode, operands[1]);
+ op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+
+ if (!reg_or_am33_const_operand (op2h, SImode))
+ op2h = force_reg (SImode, op2h);
+
+ emit_insn (gen_adddi3_internal (op0l, op0h, op1l, op2l, op1h, op2h));
+ DONE;
+})
+
+;; Note that reload only supports one commutative operand. Thus we cannot
+;; auto-swap both the high and low outputs with their matching constraints.
+;; For MN103, we're strapped for registers but thankfully the alternatives
+;; are few. For AM33, it becomes much easier to not represent the early
+;; clobber and 6 permutations of immediate and three-operand adds, but
+;; instead allocate a scratch register and do the expansion by hand.
+
+(define_insn_and_split "adddi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=r, r, r")
+ (plus:SI (match_operand:SI 2 "register_operand" "%0, 0, r")
+ (match_operand:SI 3 "nonmemory_operand" "ri,ri,ri")))
+ (set (match_operand:SI 1 "register_operand" "=D, D, r")
+ (plus:SI
+ (plus:SI
+ (ltu:SI (plus:SI (match_dup 2) (match_dup 3)) (match_dup 2))
+ (match_operand:SI 4 "register_operand" " 1, D, r"))
+ (match_operand:SI 5 "reg_or_am33_const_operand" " D, 1,ri")))
+ (clobber (match_scratch:SI 6 "=X, X,&r"))
+ (clobber (reg:CC CC_REG))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ rtx op0l = operands[0];
+ rtx op0h = operands[1];
+ rtx op1l = operands[2];
+ rtx op2l = operands[3];
+ rtx op1h = operands[4];
+ rtx op2h = operands[5];
+ rtx scratch = operands[6];
+ rtx x;
+
+ if (reg_overlap_mentioned_p (op0l, op1h))
+ {
+ emit_move_insn (scratch, op0l);
+ op1h = scratch;
+ if (reg_overlap_mentioned_p (op0l, op2h))
+ op2h = scratch;
+ }
+ else if (reg_overlap_mentioned_p (op0l, op2h))
+ {
+ emit_move_insn (scratch, op0l);
+ op2h = scratch;
+ }
+
+ if (rtx_equal_p (op0l, op1l))
+ ;
+ else if (rtx_equal_p (op0l, op2l))
+ x = op1l, op1l = op2l, op2l = x;
+ else
+ {
+ gcc_assert (TARGET_AM33);
+ if (!REG_P (op2l))
+ {
+ emit_move_insn (op0l, op2l);
+ op2l = op1l;
+ op1l = op0l;
+ }
+ }
+ emit_insn (gen_addsi3_flags (op0l, op1l, op2l));
+
+ if (rtx_equal_p (op0h, op1h))
+ ;
+ else if (rtx_equal_p (op0h, op2h))
+ x = op1h, op1h = op2h, op2h = x;
+ else
+ {
+ gcc_assert (TARGET_AM33);
+ if (!REG_P (op2h))
+ {
+ emit_move_insn (op0h, op2h);
+ op2h = op1h;
+ op1h = op0h;
+ }
+ }
+ emit_insn (gen_addc_internal (op0h, op1h, op2h));
+ DONE;
+}
+ [(set_attr "isa" "*,*,am33")]
+)
+
+;; The following pattern is generated by combine when it proves that one
+;; of the inputs to the low-part of the double-word add is zero, and thus
+;; no carry is generated into the high-part.
+
+(define_insn_and_split "*adddi3_degenerate"
+ [(set (match_operand:SI 0 "register_operand" "=&r,&r")
+ (match_operand:SI 2 "nonmemory_operand" " 0, 0"))
+ (set (match_operand:SI 1 "register_operand" "=r , r")
+ (plus:SI (match_operand:SI 3 "register_operand" "%1 , r")
+ (match_operand:SI 4 "nonmemory_operand" "ri, r")))
+ (clobber (reg:CC CC_REG))]
+ ""
+ "#"
+ ""
+ [(const_int 0)]
+{
+ rtx scratch = NULL_RTX;
+ if (!rtx_equal_p (operands[0], operands[2]))
+ {
+ gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1]));
+ if (reg_overlap_mentioned_p (operands[0], operands[3])
+ || reg_overlap_mentioned_p (operands[0], operands[4]))
+ {
+ scratch = gen_reg_rtx (SImode);
+ emit_move_insn (scratch, operands[2]);
+ }
+ else
+ emit_move_insn (operands[0], operands[2]);
+ }
+ emit_insn (gen_addsi3 (operands[1], operands[3], operands[4]));
+ if (scratch)
+ emit_move_insn (operands[0], scratch);
+ DONE;
+})
+
;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------
@@ -575,6 +741,161 @@
(set_attr "timings" "11,22")]
)
+;; A helper to expand the above, with the CC_MODE filled in.
+(define_expand "subsi3_flags"
+ [(parallel [(set (match_operand:SI 0 "register_operand")
+ (minus:SI (match_operand:SI 1 "register_operand")
+ (match_operand:SI 2 "nonmemory_operand")))
+ (set (reg:CCZNC CC_REG)
+ (compare:CCZNC (minus:SI (match_dup 1) (match_dup 2))
+ (const_int 0)))])]
+ ""
+)
+
+(define_insn "subc_internal"
+ [(set (match_operand:SI 0 "register_operand" "=D,r,r")
+ (minus:SI
+ (minus:SI (match_operand:SI 1 "register_operand" " 0,0,r")
+ (match_operand:SI 2 "reg_or_am33_const_operand" " D,i,r"))
+ (geu:SI (reg:CC CC_REG) (const_int 0))))
+ (clobber (reg:CC CC_REG))]
+ "reload_completed"
+ "@
+ subc %2,%0
+ subc %2,%0
+ subc %2,%1,%0"
+ [(set_attr "isa" "*,am33,am33")]
+)
+
+(define_expand "subdi3"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "nonmemory_operand" "")))]
+ ""
+{
+ rtx op0l, op0h, op1l, op1h, op2l, op2h;
+
+ op0l = gen_lowpart (SImode, operands[0]);
+ op1l = gen_lowpart (SImode, operands[1]);
+ op2l = gen_lowpart (SImode, operands[2]);
+ op0h = gen_highpart (SImode, operands[0]);
+ op1h = gen_highpart (SImode, operands[1]);
+ op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+
+ if (!reg_or_am33_const_operand (op2h, SImode))
+ op2h = force_reg (SImode, op2h);
+
+ emit_insn (gen_subdi3_internal (op0l, op0h, op1l, op1h, op2l, op2h));
+ DONE;
+})
+
+;; As with adddi3, the use of the scratch register helps reduce the
+;; number of permutations for AM33.
+;; ??? The early clobber on op0 avoids a reload bug wherein both output
+;; registers are set the same. Consider negate, where both op2 and op3
+;; are 0, are csed to the same input register, and reload fails to undo
+;; the cse when satisfying the matching constraints.
+
+(define_insn_and_split "subdi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=&r, r")
+ (minus:SI
+ (match_operand:SI 2 "register_operand" " 0, r")
+ (match_operand:SI 4 "nonmemory_operand" " ri,ri")))
+ (set (match_operand:SI 1 "register_operand" "=D , r")
+ (minus:SI
+ (minus:SI
+ (match_operand:SI 3 "register_operand" " 1, r")
+ (match_operand:SI 5 "reg_or_am33_const_operand" " D,ri"))
+ (ltu:SI (match_dup 2) (match_dup 4))))
+ (clobber (match_scratch:SI 6 "=X ,&r"))
+ (clobber (reg:CC CC_REG))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ rtx op0l = operands[0];
+ rtx op0h = operands[1];
+ rtx op1l = operands[2];
+ rtx op1h = operands[3];
+ rtx op2l = operands[4];
+ rtx op2h = operands[5];
+ rtx scratch = operands[6];
+
+ if (reg_overlap_mentioned_p (op0l, op1h))
+ {
+ emit_move_insn (scratch, op0l);
+ op1h = scratch;
+ if (reg_overlap_mentioned_p (op0l, op2h))
+ op2h = scratch;
+ }
+ else if (reg_overlap_mentioned_p (op0l, op2h))
+ {
+ emit_move_insn (scratch, op0l);
+ op2h = scratch;
+ }
+
+ if (!rtx_equal_p (op0l, op1l))
+ {
+ gcc_assert (TARGET_AM33);
+ if (!REG_P (op2l))
+ {
+ emit_move_insn (op0l, op1l);
+ op1l = op0l;
+ }
+ }
+ emit_insn (gen_subsi3_flags (op0l, op1l, op2l));
+
+ if (!rtx_equal_p (op0h, op1h))
+ {
+ gcc_assert (TARGET_AM33);
+ if (!REG_P (op2h))
+ {
+ emit_move_insn (op0h, op1h);
+ op1h = op0h;
+ }
+ }
+ emit_insn (gen_subc_internal (op0h, op1h, op2h));
+ DONE;
+}
+ [(set_attr "isa" "*,am33")]
+)
+
+;; The following pattern is generated by combine when it proves that one
+;; of the inputs to the low-part of the double-word sub is zero, and thus
+;; no carry is generated into the high-part.
+
+(define_insn_and_split "*subdi3_degenerate"
+ [(set (match_operand:SI 0 "register_operand" "=&r,&r")
+ (match_operand:SI 2 "nonmemory_operand" " 0, 0"))
+ (set (match_operand:SI 1 "register_operand" "=r , r")
+ (minus:SI (match_operand:SI 3 "register_operand" " 1, r")
+ (match_operand:SI 4 "nonmemory_operand" " ri, r")))
+ (clobber (reg:CC CC_REG))]
+ ""
+ "#"
+ ""
+ [(const_int 0)]
+{
+ rtx scratch = NULL_RTX;
+ if (!rtx_equal_p (operands[0], operands[2]))
+ {
+ gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1]));
+ if (reg_overlap_mentioned_p (operands[0], operands[3])
+ || reg_overlap_mentioned_p (operands[0], operands[4]))
+ {
+ scratch = gen_reg_rtx (SImode);
+ emit_move_insn (scratch, operands[2]);
+ }
+ else
+ emit_move_insn (operands[0], operands[2]);
+ }
+ emit_insn (gen_subsi3 (operands[1], operands[3], operands[4]));
+ if (scratch)
+ emit_move_insn (operands[0], scratch);
+ DONE;
+})
+
(define_insn_and_split "negsi2"
[(set (match_operand:SI 0 "register_operand" "=D,&r")
(neg:SI (match_operand:SI 1 "register_operand" " 0, r")))
diff --git a/gcc/config/mn10300/predicates.md b/gcc/config/mn10300/predicates.md
index 4badebb..8316990 100644
--- a/gcc/config/mn10300/predicates.md
+++ b/gcc/config/mn10300/predicates.md
@@ -43,6 +43,11 @@
|| XEXP (op, 1) == stack_pointer_rtx;
})
+(define_predicate "reg_or_am33_const_operand"
+ (ior (match_operand 0 "register_operand")
+ (and (match_test "TARGET_AM33")
+ (match_operand 0 "immediate_operand"))))
+
(define_predicate "label_ref_operand"
(match_code "label_ref"))