NetBSD/gnu/dist/gcc/config/arm/thumb.md

32 KiB

;; thumb.md Machine description for ARM/Thumb processors ;; Copyright (C) 1996, 1997, 1998 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 "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 "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 "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 "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 "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 "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 "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 "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); } 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,4")])

;; 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 "register_operand" "=l") (plus:DI (match_operand:DI 1 "register_operand" "%0") (match_operand:DI 2 "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 "register_operand" "=l,l,l,*r,*h,l,!k") (plus:SI (match_operand:SI 1 "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 "register_operand" "=l") (minus:DI (match_operand:DI 1 "register_operand" "0") (match_operand:DI 2 "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 "register_operand" "=l") (minus:SI (match_operand:SI 1 "register_operand" "l") (match_operand:SI 2 "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 "register_operand" "=&l,&l,&l") (mult:SI (match_operand:SI 1 "register_operand" "%l,h,0") (match_operand:SI 2 "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 "register_operand" "=l") (neg:SI (match_operand:SI 1 "register_operand" "l")))] "" "neg\t%0, %1")

;; Logical insns

(define_expand "andsi3" [(set (match_operand:SI 0 "register_operand" "") (and:SI (match_operand:SI 1 "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 "register_operand" "=l") (and:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "register_operand" "l")))] "" "and\t%0, %0, %2")

(define_insn "bicsi3" [(set (match_operand:SI 0 "register_operand" "=l") (and:SI (not:SI (match_operand:SI 1 "register_operand" "l")) (match_operand:SI 2 "register_operand" "0")))] "" "bic\t%0, %0, %1")

(define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=l") (ior:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "register_operand" "l")))] "" "orr\t%0, %0, %2")

(define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=l") (xor:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "register_operand" "l")))] "" "eor\t%0, %0, %2")

(define_insn "one_cmplsi2" [(set (match_operand:SI 0 "register_operand" "=l") (not:SI (match_operand:SI 1 "register_operand" "l")))] "" "mvn\t%0, %1")

;; Shift and rotation insns

(define_insn "ashlsi3" [(set (match_operand:SI 0 "register_operand" "=l,l") (ashift:SI (match_operand:SI 1 "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 "register_operand" "=l,l") (ashiftrt:SI (match_operand:SI 1 "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 "register_operand" "=l,l") (lshiftrt:SI (match_operand:SI 1 "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 "register_operand" "=l") (rotatert:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "register_operand" "l")))] "" "ror\t%0, %0, %2")

;; Comparison insns

(define_expand "cmpsi" [(set (cc0) (compare (match_operand:SI 0 "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 "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 "register_operand" "l"))] "" "cmp\t%0, #0")

(define_insn "cmnsi" [(set (cc0) (compare (match_operand:SI 0 "register_operand" "l") (neg:SI (match_operand:SI 1 "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 "register_operand" "l*r"))] "" "mov\tpc, %0")

(define_insn "tablejump" [(set (pc) (match_operand:SI 0 "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" [(call (match_operand:SI 0 "memory_operand" "") (match_operand 1 "" ""))] "" "")

(define_insn "call_indirect" [(call (mem:SI (match_operand:SI 0 "register_operand" "lr")) (match_operand 1 "" ""))] "! 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" [(call (mem:SI (match_operand:SI 0 "register_operand" "lr")) (match_operand 1 "" ""))] "TARGET_CALLER_INTERWORKING" "bl\t__interwork_call_via_%0" [(set_attr "length" "4")])

(define_expand "call_value" [(set (match_operand 0 "" "") (call (match_operand 1 "memory_operand" "") (match_operand 2 "" "")))] "" "")

(define_insn "call_value_indirect" [(set (match_operand 0 "" "=l") (call (mem:SI (match_operand:SI 1 "register_operand" "lr")) (match_operand 2 "" "")))] "! TARGET_CALLER_INTERWORKING" "bl\t__call_via_%1" [(set_attr "length" "4")]) ;; See comment for call_indirect pattern

(define_insn "call_value_indirect_interwork" [(set (match_operand 0 "" "=l") (call (mem:SI (match_operand:SI 1 "register_operand" "lr")) (match_operand 2 "" "")))] "TARGET_CALLER_INTERWORKING" "bl\t__interwork_call_via_%1" [(set_attr "length" "4")])

(define_insn "*call_insn" [(call (mem:SI (match_operand:SI 0 "" "i")) (match_operand:SI 1 "" ""))] "GET_CODE (operands[0]) == SYMBOL_REF" "bl\t%a0" [(set_attr "length" "4")])

(define_insn "*call_value_insn" [(set (match_operand 0 "register_operand" "=l") (call (mem:SI (match_operand 1 "" "i")) (match_operand 2 "" "")))] "GET_CODE (operands[1]) == SYMBOL_REF" "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 ""; ")