diff options
Diffstat (limited to 'gcc_arm/config/arm/thumb_020428.md')
-rwxr-xr-x | gcc_arm/config/arm/thumb_020428.md | 1194 |
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 \"\"; +") |