summaryrefslogtreecommitdiff
path: root/gcc_arm/config/arm/thumb_020428.md
diff options
context:
space:
mode:
Diffstat (limited to 'gcc_arm/config/arm/thumb_020428.md')
-rwxr-xr-xgcc_arm/config/arm/thumb_020428.md1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/gcc_arm/config/arm/thumb_020428.md b/gcc_arm/config/arm/thumb_020428.md
new file mode 100755
index 0000000..dedf42e
--- /dev/null
+++ b/gcc_arm/config/arm/thumb_020428.md
@@ -0,0 +1,1194 @@
+;; thumb.md Machine description for ARM/Thumb processors
+;; Copyright (C) 1996, 1997, 1998, 2002 Free Software Foundation, Inc.
+;; The basis of this contribution was generated by
+;; Richard Earnshaw, Advanced RISC Machines Ltd
+
+;; This file is part of GNU CC.
+
+;; GNU CC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU CC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU CC; see the file COPYING. If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; LENGTH of an instruction is 2 bytes
+(define_attr "length" "" (const_int 2))
+
+;; CONDS is set to UNCHANGED when an insn does not affect the condition codes
+;; Most insns change the condition codes
+(define_attr "conds" "changed,unchanged" (const_string "changed"))
+
+;; FAR_JUMP is "yes" if a BL instruction is used to generate a branch to a
+;; distant label.
+(define_attr "far_jump" "yes,no" (const_string "no"))
+
+;; Start with move insns
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (SImode, operands[1]);
+ }
+")
+
+(define_insn "*movsi_insn"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=l,l,l,l,l,>,l,m,*r,*h")
+ (match_operand:SI 1 "general_operand" "l,I,J,K,>,l,mi,l,*h,*r"))]
+ "register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode)"
+ "@
+ add\\t%0, %1, #0
+ mov\\t%0, %1
+ #
+ #
+ ldmia\\t%1, {%0}
+ stmia\\t%0, {%1}
+ ldr\\t%0, %1
+ str\\t%1, %0
+ mov\\t%0, %1
+ mov\\t%0, %1"
+[(set_attr "length" "2,2,4,4,2,2,2,2,2,2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "const_int_operand" ""))]
+ "thumb_shiftable_const (INTVAL (operands[1]))"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))]
+ "
+{
+ unsigned HOST_WIDE_INT val = INTVAL (operands[1]);
+ unsigned HOST_WIDE_INT mask = 0xff;
+ int i;
+ for (i = 0; i < 25; i++)
+ if ((val & (mask << i)) == val)
+ break;
+
+ if (i == 0)
+ FAIL;
+
+ operands[1] = GEN_INT (val >> i);
+ operands[2] = GEN_INT (i);
+}")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "const_int_operand" ""))]
+ "INTVAL (operands[1]) < 0 && INTVAL (operands[1]) > -256"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (neg:SI (match_dup 0)))]
+ "
+ operands[1] = GEN_INT (- INTVAL (operands[1]));
+")
+
+;;(define_expand "reload_outsi"
+;; [(set (match_operand:SI 2 "register_operand" "=&l")
+;; (match_operand:SI 1 "register_operand" "h"))
+;; (set (match_operand:SI 0 "reload_memory_operand" "=o")
+;; (match_dup 2))]
+;; ""
+;; "
+;;/* thumb_reload_out_si (operands);
+;; DONE; */
+;;")
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "general_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (HImode, operands[1]);
+
+ /* ??? We shouldn't really get invalid addresses here, but this can
+ happen if we are passed a SP (never OK for HImode/QImode) or virtual
+ register (rejected by GO_IF_LEGITIMATE_ADDRESS for HImode/QImode)
+ relative address. */
+ /* ??? This should perhaps be fixed elsewhere, for instance, in
+ fixup_stack_1, by checking for other kinds of invalid addresses,
+ e.g. a bare reference to a virtual register. This may confuse the
+ alpha though, which must handle this case differently. */
+ if (GET_CODE (operands[0]) == MEM
+ && ! memory_address_p (GET_MODE (operands[0]),
+ XEXP (operands[0], 0)))
+ {
+ rtx temp = copy_to_reg (XEXP (operands[0], 0));
+ operands[0] = change_address (operands[0], VOIDmode, temp);
+ }
+ if (GET_CODE (operands[1]) == MEM
+ && ! memory_address_p (GET_MODE (operands[1]),
+ XEXP (operands[1], 0)))
+ {
+ rtx temp = copy_to_reg (XEXP (operands[1], 0));
+ operands[1] = change_address (operands[1], VOIDmode, temp);
+ }
+ }
+ /* Handle loading a large integer during reload */
+ else if (GET_CODE (operands[1]) == CONST_INT
+ && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I'))
+ {
+ /* Writing a constant to memory needs a scratch, which should
+ be handled with SECONDARY_RELOADs. */
+ if (GET_CODE (operands[0]) != REG)
+ abort ();
+
+ operands[0] = gen_rtx (SUBREG, SImode, operands[0], 0);
+ emit_insn (gen_movsi (operands[0], operands[1]));
+ DONE;
+ }
+}")
+
+(define_insn "*movhi_insn"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=l,l,m,*r,*h,l")
+ (match_operand:HI 1 "general_operand" "l,m,l,*h,*r,I"))]
+ "register_operand (operands[0], HImode)
+ || register_operand (operands[1], HImode)"
+ "@
+ add\\t%0, %1, #0
+ ldrh\\t%0, %1
+ strh\\t%1, %0
+ mov\\t%0, %1
+ mov\\t%0, %1
+ mov\\t%0, %1")
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (QImode, operands[1]);
+
+ /* ??? We shouldn't really get invalid addresses here, but this can
+ happen if we are passed a SP (never OK for HImode/QImode) or virtual
+ register (rejected by GO_IF_LEGITIMATE_ADDRESS for HImode/QImode)
+ relative address. */
+ /* ??? This should perhaps be fixed elsewhere, for instance, in
+ fixup_stack_1, by checking for other kinds of invalid addresses,
+ e.g. a bare reference to a virtual register. This may confuse the
+ alpha though, which must handle this case differently. */
+ if (GET_CODE (operands[0]) == MEM
+ && ! memory_address_p (GET_MODE (operands[0]),
+ XEXP (operands[0], 0)))
+ {
+ rtx temp = copy_to_reg (XEXP (operands[0], 0));
+ operands[0] = change_address (operands[0], VOIDmode, temp);
+ }
+ if (GET_CODE (operands[1]) == MEM
+ && ! memory_address_p (GET_MODE (operands[1]),
+ XEXP (operands[1], 0)))
+ {
+ rtx temp = copy_to_reg (XEXP (operands[1], 0));
+ operands[1] = change_address (operands[1], VOIDmode, temp);
+ }
+ }
+ /* Handle loading a large integer during reload */
+ else if (GET_CODE (operands[1]) == CONST_INT
+ && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I'))
+ {
+ /* Writing a constant to memory needs a scratch, which should
+ be handled with SECONDARY_RELOADs. */
+ if (GET_CODE (operands[0]) != REG)
+ abort ();
+
+ operands[0] = gen_rtx (SUBREG, SImode, operands[0], 0);
+ emit_insn (gen_movsi (operands[0], operands[1]));
+ DONE;
+ }
+}")
+
+(define_insn "*movqi_insn"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=l,l,m,*r,*h,l")
+ (match_operand:QI 1 "general_operand" "l,m,l,*h,*r,I"))]
+ "register_operand (operands[0], QImode)
+ || register_operand (operands[1], QImode)"
+ "@
+ add\\t%0, %1, #0
+ ldrb\\t%0, %1
+ strb\\t%1, %0
+ mov\\t%0, %1
+ mov\\t%0, %1
+ mov\\t%0, %1")
+
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+ "
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (DImode, operands[1]);
+ }
+")
+
+;;; ??? This should have alternatives for constants.
+;;; ??? This was originally identical to the movdf_insn pattern.
+;;; ??? The 'i' constraint looks funny, but it should always be replaced by
+;;; thumb_reorg with a memory reference.
+(define_insn "*movdi_insn"
+ [(set (match_operand:DI 0 "general_operand" "=l,l,l,l,>,l,m,*r")
+ (match_operand:DI 1 "general_operand" "l,I,J,>,l,mi,l,*r"))]
+ "register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode)"
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (REGNO (operands[1]) == REGNO (operands[0]) + 1)
+ return \"add\\t%0, %1, #0\;add\\t%H0, %H1, #0\";
+ return \"add\\t%H0, %H1, #0\;add\\t%0, %1, #0\";
+ case 1:
+ return \"mov\\t%Q0, %1\;mov\\t%R0, #0\";
+ case 2:
+ operands[1] = GEN_INT (- INTVAL (operands[1]));
+ return \"mov\\t%Q0, %1\;neg\\t%Q0, %Q0\;asr\\t%R0, %Q0, #31\";
+ case 3:
+ return \"ldmia\\t%1, {%0, %H0}\";
+ case 4:
+ return \"stmia\\t%0, {%1, %H1}\";
+ case 5:
+ return thumb_load_double_from_address (operands);
+ case 6:
+ operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[0], 0), 4));
+ output_asm_insn (\"str\\t%1, %0\;str\\t%H1, %2\", operands);
+ return \"\";
+ case 7:
+ if (REGNO (operands[1]) == REGNO (operands[0]) + 1)
+ return \"mov\\t%0, %1\;mov\\t%H0, %H1\";
+ return \"mov\\t%H0, %H1\;mov\\t%0, %1\";
+ }
+}"[(set_attr "length" "4,4,6,2,2,6,4,4")])
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "general_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+ "
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (DFmode, operands[1]);
+ }
+")
+
+;;; ??? This should have alternatives for constants.
+;;; ??? This was originally identical to the movdi_insn pattern.
+;;; ??? The 'F' constraint looks funny, but it should always be replaced by
+;;; thumb_reorg with a memory reference.
+(define_insn "*movdf_insn"
+ [(set (match_operand:DF 0 "general_operand" "=l,l,>,l,m,*r")
+ (match_operand:DF 1 "general_operand" "l,>,l,mF,l,*r"))]
+ "register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode)"
+ "*
+ switch (which_alternative)
+ {
+ case 0:
+ if (REGNO (operands[1]) == REGNO (operands[0]) + 1)
+ return \"add\\t%0, %1, #0\;add\\t%H0, %H1, #0\";
+ return \"add\\t%H0, %H1, #0\;add\\t%0, %1, #0\";
+ case 1:
+ return \"ldmia\\t%1, {%0, %H0}\";
+ case 2:
+ return \"stmia\\t%0, {%1, %H1}\";
+ case 3:
+ return thumb_load_double_from_address (operands);
+ case 4:
+ operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[0], 0), 4));
+ output_asm_insn (\"str\\t%1, %0\;str\\t%H1, %2\", operands);
+ return \"\";
+ case 5:
+ if (REGNO (operands[1]) == REGNO (operands[0]) + 1)
+ return \"mov\\t%0, %1\;mov\\t%H0, %H1\";
+ return \"mov\\t%H0, %H1\;mov\\t%0, %1\";
+ }
+"[(set_attr "length" "4,2,2,6,4,4")])
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "general_operand" "")
+ (match_operand:SF 1 "general_operand" ""))]
+ ""
+ "
+ if (! (reload_in_progress || reload_completed))
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[1] = force_reg (SFmode, operands[1]);
+ }
+")
+
+;;; ??? This should have alternatives for constants.
+(define_insn "*movsf_insn"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=l,l,>,l,m,*r,*h")
+ (match_operand:SF 1 "general_operand" "l,>,l,mF,l,*h,*r"))]
+ "register_operand (operands[0], SFmode)
+ || register_operand (operands[1], SFmode)"
+ "@
+ add\\t%0, %1, #0
+ ldmia\\t%1, {%0}
+ stmia\\t%0, {%1}
+ ldr\\t%0, %1
+ str\\t%1, %0
+ mov\\t%0, %1
+ mov\\t%0, %1")
+
+;; Widening move insns
+
+(define_expand "zero_extendhisi2"
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ operands[1] = force_reg (HImode, operands[1]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ emit_insn (gen_ashlsi3 (temp, operands[1], GEN_INT (16)));
+ emit_insn (gen_lshrsi3 (operands[0], temp, GEN_INT (16)));
+ DONE;
+ }
+")
+
+(define_insn "*zero_extendhisi2_insn"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
+ ""
+ "ldrh\\t%0, %1")
+
+(define_expand "zero_extendqisi2"
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ operands[1] = force_reg (QImode, operands[1]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ emit_insn (gen_ashlsi3 (temp, operands[1], GEN_INT (24)));
+ emit_insn (gen_lshrsi3 (operands[0], temp, GEN_INT (24)));
+ DONE;
+ }
+")
+
+(define_insn "*zero_extendqisi2_insn"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
+ ""
+ "ldrb\\t%0, %1")
+
+(define_expand "extendhisi2"
+ [(parallel [(set (match_operand:SI 0 "s_register_operand" "")
+ (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:SI 2 ""))])]
+ ""
+ "
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ operands[1] = force_reg (HImode, operands[1]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ emit_insn (gen_ashlsi3 (temp, operands[1], GEN_INT (16)));
+ emit_insn (gen_ashrsi3 (operands[0], temp, GEN_INT (16)));
+ DONE;
+ }
+")
+
+(define_insn "*extendhisi2_insn"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))
+ (clobber (match_scratch:SI 2 "=&l"))]
+ ""
+ "*
+{
+ rtx ops[4];
+ /* This code used to try to use 'V', and fix the address only if it was
+ offsettable, but this fails for e.g. REG+48 because 48 is outside the
+ range of QImode offsets, and offsettable_address_p does a QImode
+ address check. */
+
+ if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
+ {
+ ops[1] = XEXP (XEXP (operands[1], 0), 0);
+ ops[2] = XEXP (XEXP (operands[1], 0), 1);
+ }
+ else
+ {
+ ops[1] = XEXP (operands[1], 0);
+ ops[2] = const0_rtx;
+ }
+ if (GET_CODE (ops[2]) == REG)
+ return \"ldrsh\\t%0, %1\";
+
+ ops[0] = operands[0];
+ ops[3] = operands[2];
+ output_asm_insn (\"mov\\t%3, %2\;ldrsh\\t%0, [%1, %3]\", ops);
+ return \"\";
+}"
+[(set_attr "length" "4")])
+
+(define_expand "extendqisi2"
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ operands[1] = force_reg (QImode, operands[1]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ emit_insn (gen_ashlsi3 (temp, operands[1], GEN_INT (24)));
+ emit_insn (gen_ashrsi3 (operands[0], temp, GEN_INT (24)));
+ DONE;
+ }
+")
+
+(define_insn "*extendqisi2_insn"
+ [(set (match_operand:SI 0 "s_register_operand" "=l,l")
+ (sign_extend:SI (match_operand:QI 1 "memory_operand" "V,m")))]
+ ""
+ "*
+{
+ rtx ops[3];
+
+ if (which_alternative == 0)
+ return \"ldrsb\\t%0, %1\";
+ ops[0] = operands[0];
+ if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
+ {
+ ops[1] = XEXP (XEXP (operands[1], 0), 0);
+ ops[2] = XEXP (XEXP (operands[1], 0), 1);
+
+ if (GET_CODE (ops[1]) == REG && GET_CODE (ops[2]) == REG)
+ output_asm_insn (\"ldrsb\\t%0, [%1, %2]\", ops);
+ else if (GET_CODE (ops[1]) == REG)
+ {
+ if (REGNO (ops[1]) == REGNO (operands[0]))
+ output_asm_insn (\"ldrb\\t%0, [%1, %2]\;lsl\\t%0, %0, #24\;asr\\t%0, %0, #24\", ops);
+ else
+ output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops);
+ }
+ else
+ {
+ if (REGNO (ops[2]) == REGNO (operands[0]))
+ output_asm_insn (\"ldrb\\t%0, [%2, %1]\;lsl\\t%0, %0, #24\;asr\\t%0, %0, #24\", ops);
+ else
+ output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops);
+ }
+ }
+ else if (REGNO (operands[0]) == REGNO (XEXP (operands[1], 0)))
+ {
+ output_asm_insn (\"ldrb\\t%0, [%0, #0]\;lsl\\t%0, %0, #24\;asr\\t%0, %0, #24\", ops);
+ }
+ else
+ {
+ ops[1] = XEXP (operands[1], 0);
+ ops[2] = const0_rtx;
+ output_asm_insn (\"mov\\t%0, %2\;ldrsb\\t%0, [%1, %0]\", ops);
+ }
+ return \"\";
+}"
+[(set_attr "length" "2,6")])
+
+;; We don't really have extzv, but defining this using shifts helps
+;; to reduce register pressure later on.
+
+(define_expand "extzv"
+ [(set (match_dup 4)
+ (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))
+ (set (match_operand:SI 0 "register_operand" "")
+ (lshiftrt:SI (match_dup 4)
+ (match_operand:SI 3 "const_int_operand" "")))]
+ ""
+ "
+{
+ HOST_WIDE_INT lshift = 32 - INTVAL (operands[2]) - INTVAL (operands[3]);
+ HOST_WIDE_INT rshift = 32 - INTVAL (operands[2]);
+ operands[3] = GEN_INT (rshift);
+ if (lshift == 0)
+ {
+ emit_insn (gen_lshrsi3 (operands[0], operands[1], operands[3]));
+ DONE;
+ }
+ operands[2] = GEN_INT (lshift);
+ operands[4] = gen_reg_rtx (SImode);
+}
+")
+
+;; Block-move insns
+
+(define_expand "movstrqi"
+ [(match_operand:BLK 0 "general_operand" "")
+ (match_operand:BLK 1 "general_operand" "")
+ (match_operand:SI 2 "" "")
+ (match_operand:SI 3 "const_int_operand" "")]
+ ""
+ "
+ if (INTVAL (operands[3]) != 4
+ || GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) > 48)
+ FAIL;
+
+ thumb_expand_movstrqi (operands);
+ DONE;
+")
+
+(define_insn "movmem12b"
+ [(set (mem:SI (match_operand:SI 0 "register_operand" "+&l"))
+ (mem:SI (match_operand:SI 1 "register_operand" "+&l")))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 4)))
+ (mem:SI (plus:SI (match_dup 1) (const_int 4))))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 8)))
+ (mem:SI (plus:SI (match_dup 1) (const_int 8))))
+ (set (match_dup 0) (plus:SI (match_dup 0) (const_int 12)))
+ (set (match_dup 1) (plus:SI (match_dup 1) (const_int 12)))
+ (clobber (match_scratch:SI 2 "=&l"))
+ (clobber (match_scratch:SI 3 "=&l"))
+ (clobber (match_scratch:SI 4 "=&l"))]
+ ""
+ "* return output_move_mem_multiple (3, operands);"
+[(set_attr "length" "4")])
+
+(define_insn "movmem8b"
+ [(set (mem:SI (match_operand:SI 0 "register_operand" "+&l"))
+ (mem:SI (match_operand:SI 1 "register_operand" "+&l")))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 4)))
+ (mem:SI (plus:SI (match_dup 1) (const_int 4))))
+ (set (match_dup 0) (plus:SI (match_dup 0) (const_int 8)))
+ (set (match_dup 1) (plus:SI (match_dup 1) (const_int 8)))
+ (clobber (match_scratch:SI 2 "=&l"))
+ (clobber (match_scratch:SI 3 "=&l"))]
+ ""
+ "* return output_move_mem_multiple (2, operands);"
+[(set_attr "length" "4")])
+
+;; Arithmetic insns
+
+(define_insn "adddi3"
+ [(set (match_operand:DI 0 "s_register_operand" "=l")
+ (plus:DI (match_operand:DI 1 "s_register_operand" "%0")
+ (match_operand:DI 2 "s_register_operand" "l")))]
+ ""
+ "add\\t%Q0, %Q0, %Q2\;adc\\t%R0, %R0, %R2"
+[(set_attr "conds" "changed")
+ (set_attr "length" "8")])
+
+;; register group 'k' is a single register group containing only the stack
+;; register. Trying to reload it will always fail catastrophically,
+;; so never allow those alternatives to match if reloading is needed.
+(define_insn "addsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l,l,l,*r,*h,l,!k")
+ (plus:SI (match_operand:SI 1 "s_register_operand" "%0,0,l,*0,*0,!k,!k")
+ (match_operand:SI 2 "nonmemory_operand" "I,J,lL,*h,*r,!M,!O")))]
+ ""
+ "*
+ static char *asms[] =
+{
+ \"add\\t%0, %0, %2\",
+ \"sub\\t%0, %0, #%n2\",
+ \"add\\t%0, %1, %2\",
+ \"add\\t%0, %0, %2\",
+ \"add\\t%0, %0, %2\",
+ \"add\\t%0, %1, %2\",
+ \"add\\t%0, %1, %2\"
+};
+ if (which_alternative == 2 && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) < 0)
+ return \"sub\\t%0, %1, #%n2\";
+ return asms[which_alternative];
+")
+
+; reloading and elimination of the frame pointer can sometimes cause this
+; optimization to be missed.
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (match_operand:SI 1 "const_int_operand" "M"))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0) (match_operand:SI 2 "register_operand" "k")))]
+ "REGNO (operands[2]) == STACK_POINTER_REGNUM
+ && (unsigned HOST_WIDE_INT) (INTVAL (operands[1])) < 1024
+ && (INTVAL (operands[1]) & 3) == 0"
+ "add\\t%0, %2, %1")
+
+(define_insn "subdi3"
+ [(set (match_operand:DI 0 "s_register_operand" "=l")
+ (minus:DI (match_operand:DI 1 "s_register_operand" "0")
+ (match_operand:DI 2 "s_register_operand" "l")))]
+ ""
+ "sub\\t%Q0, %Q0, %Q2\;sbc\\t%R0, %R0, %R2"
+[(set_attr "conds" "changed")
+ (set_attr "length" "8")])
+
+(define_insn "subsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (minus:SI (match_operand:SI 1 "s_register_operand" "l")
+ (match_operand:SI 2 "s_register_operand" "l")))]
+ ""
+ "sub\\t%0, %1, %2")
+
+;; We must ensure that one input matches the output, and that the other input
+;; does not match the output. Using 0 satisfies the first, and using &
+;; satisfies the second. Unfortunately, this fails when operands 1 and 2
+;; are the same, because reload will make operand 0 match operand 1 without
+;; realizing that this conflicts with operand 2. We fix this by adding another
+;; alternative to match this case, and then `reload' it ourselves. This
+;; alternative must come first.
+(define_insn "mulsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=&l,&l,&l")
+ (mult:SI (match_operand:SI 1 "s_register_operand" "%l,*h,0")
+ (match_operand:SI 2 "s_register_operand" "l,l,l")))]
+ ""
+ "*
+{
+ if (which_alternative < 2)
+ return \"mov\\t%0, %1\;mul\\t%0, %0, %2\";
+ else
+ return \"mul\\t%0, %0, %2\";
+}"
+ [(set_attr "length" "4,4,2")])
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (neg:SI (match_operand:SI 1 "s_register_operand" "l")))]
+ ""
+ "neg\\t%0, %1")
+
+;; Logical insns
+
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (and:SI (match_operand:SI 1 "s_register_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))]
+ ""
+ "
+ if (GET_CODE (operands[2]) != CONST_INT)
+ operands[2] = force_reg (SImode, operands[2]);
+ else
+ {
+ int i;
+ if (((unsigned HOST_WIDE_INT) ~ INTVAL (operands[2])) < 256)
+ {
+ operands[2] = force_reg (SImode, GEN_INT (~INTVAL (operands[2])));
+ emit_insn (gen_bicsi3 (operands[0], operands[2], operands[1]));
+ DONE;
+ }
+
+ for (i = 9; i <= 31; i++)
+ if ((((HOST_WIDE_INT) 1) << i) - 1 == INTVAL (operands[2]))
+ {
+ emit_insn (gen_extzv (operands[0], operands[1], GEN_INT (i),
+ const0_rtx));
+ DONE;
+ }
+ else if ((((HOST_WIDE_INT) 1) << i) - 1 == ~ INTVAL (operands[2]))
+ {
+ rtx shift = GEN_INT (i);
+ rtx reg = gen_reg_rtx (SImode);
+ emit_insn (gen_lshrsi3 (reg, operands[1], shift));
+ emit_insn (gen_ashlsi3 (operands[0], reg, shift));
+ DONE;
+ }
+
+ operands[2] = force_reg (SImode, operands[2]);
+ }
+")
+
+(define_insn "*andsi3_insn"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (and:SI (match_operand:SI 1 "s_register_operand" "%0")
+ (match_operand:SI 2 "s_register_operand" "l")))]
+ ""
+ "and\\t%0, %0, %2")
+
+(define_insn "bicsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (and:SI (not:SI (match_operand:SI 1 "s_register_operand" "l"))
+ (match_operand:SI 2 "s_register_operand" "0")))]
+ ""
+ "bic\\t%0, %0, %1")
+
+(define_insn "iorsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (ior:SI (match_operand:SI 1 "s_register_operand" "%0")
+ (match_operand:SI 2 "s_register_operand" "l")))]
+ ""
+ "orr\\t%0, %0, %2")
+
+(define_insn "xorsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (xor:SI (match_operand:SI 1 "s_register_operand" "%0")
+ (match_operand:SI 2 "s_register_operand" "l")))]
+ ""
+ "eor\\t%0, %0, %2")
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (not:SI (match_operand:SI 1 "s_register_operand" "l")))]
+ ""
+ "mvn\\t%0, %1")
+
+;; Shift and rotation insns
+
+(define_insn "ashlsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l,l")
+ (ashift:SI (match_operand:SI 1 "s_register_operand" "l,0")
+ (match_operand:SI 2 "nonmemory_operand" "N,l")))]
+ ""
+ "@
+ lsl\\t%0, %1, %2
+ lsl\\t%0, %0, %2")
+
+(define_insn "ashrsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l,l")
+ (ashiftrt:SI (match_operand:SI 1 "s_register_operand" "l,0")
+ (match_operand:SI 2 "nonmemory_operand" "N,l")))]
+ ""
+ "@
+ asr\\t%0, %1, %2
+ asr\\t%0, %0, %2")
+
+(define_insn "lshrsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l,l")
+ (lshiftrt:SI (match_operand:SI 1 "s_register_operand" "l,0")
+ (match_operand:SI 2 "nonmemory_operand" "N,l")))]
+ ""
+ "@
+ lsr\\t%0, %1, %2
+ lsr\\t%0, %0, %2")
+
+(define_insn "rotrsi3"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (rotatert:SI (match_operand:SI 1 "s_register_operand" "0")
+ (match_operand:SI 2 "s_register_operand" "l")))]
+ ""
+ "ror\\t%0, %0, %2")
+
+;; Comparison insns
+
+(define_expand "cmpsi"
+ [(set (cc0) (compare (match_operand:SI 0 "s_register_operand" "")
+ (match_operand:SI 1 "nonmemory_operand" "")))]
+ ""
+ "
+ if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
+ {
+ if (GET_CODE (operands[1]) != CONST_INT
+ || (unsigned HOST_WIDE_INT) (INTVAL (operands[1])) >= 256)
+ {
+ if (GET_CODE (operands[1]) != CONST_INT
+ || INTVAL (operands[1]) < -255
+ || INTVAL (operands[1]) > 0)
+ operands[1] = force_reg (SImode, operands[1]);
+ else
+ {
+ operands[1] = force_reg (SImode,
+ GEN_INT (- INTVAL (operands[1])));
+ emit_insn (gen_cmnsi (operands[0], operands[1]));
+ DONE;
+ }
+ }
+ }
+")
+
+(define_insn "*cmpsi_insn"
+ [(set (cc0) (compare (match_operand:SI 0 "s_register_operand" "l,*r,*h")
+ (match_operand:SI 1 "thumb_cmp_operand" "lI,*h,*r")))]
+ ""
+ "@
+ cmp\\t%0, %1
+ cmp\\t%0, %1
+ cmp\\t%0, %1")
+
+(define_insn "tstsi"
+ [(set (cc0) (match_operand:SI 0 "s_register_operand" "l"))]
+ ""
+ "cmp\\t%0, #0")
+
+(define_insn "cmnsi"
+ [(set (cc0) (compare (match_operand:SI 0 "s_register_operand" "l")
+ (neg:SI (match_operand:SI 1 "s_register_operand" "l"))))]
+ ""
+ "cmn\\t%0, %1")
+
+;; Jump insns
+
+(define_insn "jump"
+ [(set (pc) (label_ref (match_operand 0 "" "")))]
+ ""
+ "*
+ if (get_attr_length (insn) == 2)
+ return \"b\\t%l0\";
+ return \"bl\\t%l0\\t%@ far jump\";
+"[(set (attr "far_jump")
+ (if_then_else (eq_attr "length" "4")
+ (const_string "yes")
+ (const_string "no")))
+ (set (attr "length")
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -2048))
+ (le (minus (match_dup 0) (pc)) (const_int 2044)))
+ (const_int 2)
+ (const_int 4)))])
+
+
+(define_expand "beq"
+ [(set (pc) (if_then_else (eq (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bne"
+ [(set (pc) (if_then_else (ne (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bge"
+ [(set (pc) (if_then_else (ge (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "ble"
+ [(set (pc) (if_then_else (le (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bgt"
+ [(set (pc) (if_then_else (gt (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "blt"
+ [(set (pc) (if_then_else (lt (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bgeu"
+ [(set (pc) (if_then_else (geu (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bleu"
+ [(set (pc) (if_then_else (leu (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bgtu"
+ [(set (pc) (if_then_else (gtu (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_expand "bltu"
+ [(set (pc) (if_then_else (ltu (cc0) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "")
+
+(define_insn "*cond_branch"
+ [(set (pc) (if_then_else (match_operator 1 "comparison_operator"
+ [(cc0) (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+ switch (get_attr_length (insn))
+ {
+ case 2: return \"b%d1\\t%l0\\t%@cond_branch\";
+ case 4: return \"b%D1\\t.LCB%=\;b\\t%l0\\t%@long jump\\n.LCB%=:\";
+ default: return \"b%D1\\t.LCB%=\;bl\\t%l0\\t%@far jump\\n.LCB%=:\";
+ }
+"[(set (attr "far_jump")
+ (if_then_else (eq_attr "length" "6")
+ (const_string "yes")
+ (const_string "no")))
+ (set (attr "length")
+ (if_then_else
+ (and (ge (minus (match_dup 0) (pc)) (const_int -252))
+ (le (minus (match_dup 0) (pc)) (const_int 254)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -2044))
+ (le (minus (match_dup 0) (pc)) (const_int 2044)))
+ (const_int 4)
+ (const_int 6))))])
+
+(define_insn "*cond_branch_reversed"
+ [(set (pc) (if_then_else (match_operator 1 "comparison_operator"
+ [(cc0) (const_int 0)])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+ switch (get_attr_length (insn))
+ {
+ case 2: return \"b%D1\\t%l0\\t%@cond_branch_reversed\";
+ case 4: return \"b%d1\\t.LCBR%=\;b\\t%l0\\t%@long jump\\n.LCBR%=:\";
+ default: return \"b%d1\\t.LCBR%=\;bl\\t%l0\\t%@far jump\\n.LCBR%=:\";
+ }
+ return \"\";
+"[(set (attr "far_jump")
+ (if_then_else (eq_attr "length" "6")
+ (const_string "yes")
+ (const_string "no")))
+ (set (attr "length")
+ (if_then_else
+ (and (ge (minus (match_dup 0) (pc)) (const_int -252))
+ (le (minus (match_dup 0) (pc)) (const_int 254)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -2044))
+ (le (minus (match_dup 0) (pc)) (const_int 2044)))
+ (const_int 4)
+ (const_int 6))))])
+
+(define_insn "indirect_jump"
+ [(set (pc) (match_operand:SI 0 "s_register_operand" "l*r"))]
+ ""
+ "mov\\tpc, %0")
+
+(define_insn "tablejump"
+ [(set (pc) (match_operand:SI 0 "s_register_operand" "l*r"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "mov\\tpc, %0")
+
+(define_insn "return"
+ [(return)]
+ "USE_RETURN"
+ "* return output_return ();"
+[(set_attr "length" "18")])
+
+;; Call insns
+
+(define_expand "call"
+ [(parallel
+ [(call (match_operand:SI 0 "memory_operand" "")
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))])]
+ ""
+ "
+{
+ if (GET_CODE (XEXP (operands[0], 0)) != REG
+ && arm_is_longcall_p (operands[0], INTVAL (operands[2]), 0))
+ XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0));
+}")
+
+(define_insn "*call_indirect"
+ [(parallel
+ [(call (mem:SI (match_operand:SI 0 "s_register_operand" "l*r"))
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))])]
+ "! TARGET_CALLER_INTERWORKING"
+ "bl\\t%__call_via_%0"
+[(set_attr "length" "4")])
+;; The non THUMB_INTERWORK, non TARGET_CALLER_INTERWORKING version
+;; used to be: "mov\\tlr,pc\;bx\\t%0", but the mov does not set
+;; the bottom bit of lr so that a function return (using bx)
+;; would switch back into ARM mode...
+
+(define_insn "*call_indirect_interwork"
+ [(parallel
+ [(call (mem:SI (match_operand:SI 0 "s_register_operand" "l*r"))
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))])]
+ "TARGET_CALLER_INTERWORKING"
+ "bl\\t%__interwork_call_via_%0"
+[(set_attr "length" "4")])
+
+(define_expand "call_value"
+ [(parallel
+ [(set (match_operand 0 "" "")
+ (call (match_operand 1 "memory_operand" "")
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))])]
+ ""
+ "
+{
+ if (GET_CODE (XEXP (operands[1], 0)) != REG
+ && arm_is_longcall_p (operands[1], INTVAL (operands[3]), 0))
+ XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0));
+}")
+
+(define_insn "*call_value_indirect"
+ [(parallel
+ [(set (match_operand 0 "" "=l")
+ (call (mem:SI (match_operand:SI 1 "s_register_operand" "l*r"))
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))])]
+ "! TARGET_CALLER_INTERWORKING"
+ "bl\\t%__call_via_%1"
+[(set_attr "length" "4")])
+;; See comment for call_indirect pattern
+
+(define_insn "*call_value_indirect_interwork"
+ [(parallel
+ [(set (match_operand 0 "" "=l")
+ (call (mem:SI (match_operand:SI 1 "s_register_operand" "l*r"))
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))])]
+ "TARGET_CALLER_INTERWORKING"
+ "bl\\t%__interwork_call_via_%1"
+[(set_attr "length" "4")])
+
+
+(define_insn "*call_insn"
+ [(parallel
+ [(call (mem:SI (match_operand:SI 0 "" "i"))
+ (match_operand:SI 1 "" ""))
+ (use (match_operand 2 "" ""))])]
+ "GET_CODE (operands[0]) == SYMBOL_REF
+ && ! arm_is_longcall_p (operands[0], INTVAL (operands[2]), 1)"
+ "bl\\t%a0"
+[(set_attr "length" "4")])
+
+(define_insn "*call_value_insn"
+ [(parallel
+ [(set (match_operand 0 "s_register_operand" "=l")
+ (call (mem:SI (match_operand 1 "" "i"))
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))])]
+ "GET_CODE(operands[1]) == SYMBOL_REF
+ && ! arm_is_longcall_p (operands[1], INTVAL (operands[3]), 1)"
+ "bl\\t%a1"
+[(set_attr "length" "4")])
+
+;; Untyped call not required, since all funcs return in r0
+
+;; Miscellaneous patterns
+
+(define_insn "nop"
+ [(clobber (const_int 0))]
+ ""
+ "mov\\tr8, r8")
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] 0)]
+ ""
+ ""
+ [(set_attr "length" "0")])
+
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+ "
+ thumb_expand_prologue ();
+ DONE;
+")
+
+(define_expand "epilogue"
+ [(unspec_volatile [(const_int 0)] 1)]
+ "! thumb_trivial_epilogue ()"
+ "
+ thumb_expand_epilogue ();
+")
+
+(define_insn "*epilogue_insns"
+ [(unspec_volatile [(const_int 0)] 1)]
+ ""
+ "*
+ return thumb_unexpanded_epilogue ();
+"
+[(set_attr "length" "42")])
+
+;; Special patterns for dealing with the constant pool
+
+(define_insn "consttable_4"
+ [(unspec_volatile [(match_operand 0 "" "")] 2)]
+ ""
+ "*
+{
+ switch (GET_MODE_CLASS (GET_MODE (operands[0])))
+ {
+ case MODE_FLOAT:
+ {
+ union real_extract u;
+ bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+ assemble_real (u.d, GET_MODE (operands[0]));
+ break;
+ }
+ default:
+ assemble_integer (operands[0], 4, 1);
+ break;
+ }
+ return \"\";
+}"
+[(set_attr "length" "4")])
+
+(define_insn "consttable_8"
+ [(unspec_volatile [(match_operand 0 "" "")] 3)]
+ ""
+ "*
+{
+ switch (GET_MODE_CLASS (GET_MODE (operands[0])))
+ {
+ case MODE_FLOAT:
+ {
+ union real_extract u;
+ bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+ assemble_real (u.d, GET_MODE (operands[0]));
+ break;
+ }
+ default:
+ assemble_integer (operands[0], 8, 1);
+ break;
+ }
+ return \"\";
+}"
+[(set_attr "length" "8")])
+
+(define_insn "consttable_end"
+ [(unspec_volatile [(const_int 0)] 4)]
+ ""
+ "*
+ /* Nothing to do (currently). */
+ return \"\";
+")
+
+(define_insn "align_4"
+ [(unspec_volatile [(const_int 0)] 5)]
+ ""
+ "*
+ assemble_align (32);
+ return \"\";
+")