summaryrefslogtreecommitdiff
path: root/gcc/config/m68hc11/m68hc11.md
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /gcc/config/m68hc11/m68hc11.md
downloadcbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2
cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository.
Diffstat (limited to 'gcc/config/m68hc11/m68hc11.md')
-rw-r--r--gcc/config/m68hc11/m68hc11.md7579
1 files changed, 7579 insertions, 0 deletions
diff --git a/gcc/config/m68hc11/m68hc11.md b/gcc/config/m68hc11/m68hc11.md
new file mode 100644
index 000000000..f4ff3ebbb
--- /dev/null
+++ b/gcc/config/m68hc11/m68hc11.md
@@ -0,0 +1,7579 @@
+;;- Machine description file for Motorola 68HC11 and 68HC12.
+;;- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008
+;;- Free Software Foundation, Inc.
+;;- Contributed by Stephane Carrez (stcarrez@nerim.fr)
+
+;; This file is part of GCC.
+
+;; GCC 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 3, or (at your option)
+;; any later version.
+
+;; GCC 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 GCC; see the file COPYING3. If not see
+;; <http://www.gnu.org/licenses/>.
+
+;; Note:
+;; A first 68HC11 port was made by Otto Lind (otto@coactive.com)
+;; on gcc 2.6.3. I have used it as a starting point for this port.
+;; However, this new port is a complete re-write. Its internal
+;; design is completely different. The generated code is not
+;; compatible with the gcc 2.6.3 port.
+;;
+;; The gcc 2.6.3 port is available at:
+;;
+;; ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
+;;
+
+;;- Instruction patterns. When multiple patterns apply,
+;;- the first one in the file is chosen.
+;;-
+;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
+;;-
+;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
+;;- updates for most instructions.
+
+;;
+;; The following constraints are used:
+;;
+;; Single pair registers:
+;; a register 'a' 8-bit
+;; b register 'b' 8-bit
+;; d register 'd' 16-bit
+;; t pseudo soft register 'TMP' 16-bit
+;; v register 'd' for 68hc11, 16-bit
+;; NO_REG for 68hc12
+;; (used for scratch register)
+;; w register 'sp' 16-bit
+;; x register 'x' 16-bit
+;; y register 'y' 16-bit
+;; z register 'z' 16-bit (fake r for 68HC11 and 68HC12)
+;; D register 'd+x' 32-bit
+;;
+;; Group of registers:
+;; q register 'a' or 'b' or 'd' 8-bit
+;; u pseudo soft register 16-bit
+;; A register 'x', 'y', 'z' 16-bit
+;; B register 'x', 'y' 16-bit
+;; h register 'd', 'x', 'y', 'z' 16-bit
+;;
+;; Other constraints:
+;;
+;; Q an operand which is in memory but whose address is constant
+;; (i.e., a (MEM (SYMBOL_REF x))). This constraint is used by
+;; bset/bclr instructions together with linker relaxation. The
+;; operand can be translated to a page0 addressing mode if the
+;; symbol address is in page0 (0..255).
+;;
+;; R an operand which is in memory and whose address is expressed
+;; with 68HC11/68HC12 indexed addressing mode. In general this
+;; is any valid (MEM) except a (MEM (SYMBOL_REF x)).
+;;
+;; U an operand which is in memory and if it uses the 68HC12 indexed
+;; addressing mode, the offset is in the range -16..+15. This is
+;; used by 68HC12 movb/movw instructions since they do not accept
+;; the full 16-bit offset range (as other insn do).
+;;
+;;
+;; Immediate integer operand constraints:
+;; `L' is for range -65536 to 65536
+;; `M' is for values whose 16-bit low part is 0
+;; 'N' is for +1 or -1.
+;; 'O' is for 16 (for rotate using swap).
+;; 'P' is for range -8 to 2 (used by addhi_sp)
+;;
+;; In many cases, it's not possible to use the 'g' or 'r' constraints.
+;;
+;; Operands modifiers:
+;;
+;; %b Get the low part of the operand (to obtain a QImode)
+;; This modifier must always be used for QImode operations
+;; because a correction must be applied when the operand
+;; is a soft register (ex: *ZD1). Otherwise, we generate
+;; *ZD1 and this is the high part of the register. For other
+;; kinds of operands, if the operand is already QImode, no
+;; additional correction is made.
+;; %h Get the high part of the operand (to obtain a QImode)
+;; %t Represents the temporary/scratch register *_.tmp
+;; The scratch register is used in some cases when GCC puts
+;; some values in bad registers.
+;;
+;; 32/64-bit Patterns:
+;; The 68HC11 does not support 32/64-bit operations. Most of the
+;; 32/64-bit patterns are defined to split the instruction in
+;; 16-bits patterns. Providing split patterns generates better code
+;; than letting GCC implement the 32/64-bit operation itself.
+;;
+;;
+;; Notes:
+;;
+;; o For iorqi3, andqi3, xorqi3 patterns, we must accept the 'A' constraint
+;; otherwise some insn are not satisfied.
+;;
+;; o Split patterns that create a swap_areg pattern (xgdx or xgdy) must
+;; be valid only when z_replacement_completed == 2 because once these
+;; swap instructions are generated, a flow/cse pass fails to handle
+;; them correctly (it would treat the X, Y or D register as dead sometimes).
+;;
+;; o Some split pattern generate instructions that operate on 'a' or 'b'
+;; register directly (high part and low part of D respectively).
+;; Such split pattern must also be valid when z_replacement_completed == 2
+;; because flow/cse is not aware that D is composed of {a, b}.
+;;
+;; o Split patterns that generate a (mem:QI (symbol_reg _.dx)) to access
+;; the high part of a soft register must be expanded after z_replacement
+;; pass.
+;;
+;;---------------------------------------------------------------------------
+;; Constants
+
+(define_constants [
+ ;; Register numbers
+ (X_REGNUM 0) ; Index X register
+ (D_REGNUM 1) ; Data register
+ (Y_REGNUM 2) ; Index Y register
+ (SP_REGNUM 3) ; Stack pointer
+ (PC_REGNUM 4) ; Program counter
+ (A_REGNUM 5) ; A (high part of D)
+ (B_REGNUM 6) ; B (low part of D)
+ (CC_REGNUM 7) ; Condition code register
+ (SOFT_TMP_REGNUM 10) ; TMP soft register
+ (SOFT_Z_REGNUM 11) ; Z soft register
+ (SOFT_XY_REGNUM 12) ; XY soft register
+])
+
+(include "predicates.md")
+
+;;--------------------------------------------------------------------
+;;- Test
+;;--------------------------------------------------------------------
+;;
+;; The test and compare insn must not accept a memory operand with
+;; an auto-inc mode. If we do this, the reload can emit move insns
+;; after the test or compare. Such move will set the flags and therefore
+;; break the comparison. This can happen if the auto-inc register
+;; does not happen to be a hard register (i.e., reloading occurs).
+;; An offsetable memory operand should be ok. The 'tst_operand' and
+;; 'cmp_operand' predicates take care of this rule.
+;;
+
+(define_insn "tsthi_1"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "tst_operand" "dx,*y")
+ (const_int 0)))]
+ ""
+ "*
+{
+ if (D_REG_P (operands[0]) && !TARGET_M6812)
+ return \"std\\t%t0\";
+ else
+ return \"cp%0\\t#0\";
+}")
+
+;;
+;; Split pattern for (tst:QI) on an address register.
+;;
+(define_split
+ [(set (cc0)
+ (compare (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (const_int 0)))]
+ "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 1))
+ (set (match_dup 1) (reg:HI D_REGNUM))])
+ (set (cc0) (compare (reg:QI D_REGNUM)
+ (const_int 0)))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 1))
+ (set (match_dup 1) (reg:HI D_REGNUM))])]
+ "operands[1] = gen_rtx_REG (HImode, REGNO (operands[0]));")
+
+(define_insn "tstqi_1"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "tst_operand" "m,d,*A,!u")
+ (const_int 0)))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ else if (D_REG_P (operands[0]))
+ return \"tstb\";
+
+ else if (dead_register_here (insn, d_reg))
+ return \"ldab\\t%b0\";
+
+ else
+ return \"tst\\t%b0\";
+}")
+
+;;
+;; tstqi_z_used, cmpqi_z_used and cmphi_z_used are patterns generated
+;; during the Z register replacement. They are used when an operand
+;; uses the Z register as an index register (i.e., (MEM:QI (REG:HI Z))).
+;; In that case, we have to preserve the values of the replacement
+;; register (as well as the CC0 since the insns are compare insns).
+;; To do this, the replacement register is pushed on the stack and
+;; restored after the real compare. A pattern+split is defined to
+;; avoid problems with the flow+cse register pass which are made
+;; after Z register replacement.
+;;
+(define_insn_and_split "tstqi_z_used"
+ [(set (cc0) (compare (match_operand:QI 0 "tst_operand" "m")
+ (const_int 0)))
+ (use (match_operand:HI 1 "hard_reg_operand" "dxy"))
+ (use (reg:HI SOFT_Z_REGNUM))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 1))
+ (set (match_dup 1) (match_dup 2))
+ (set (cc0) (compare (match_dup 0)
+ (const_int 0)))
+ (set (match_dup 1) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "operands[2] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
+
+
+;;--------------------------------------------------------------------
+;;- Compare
+;;--------------------------------------------------------------------
+
+;;
+;; Comparison of a hard register with another one is provided because
+;; it helps GCC to avoid to spill a pseudo hard register.
+;; We use a temporary in page 0, this is equivalent to a pseudo hard reg.
+;; (except that we loose the information that the value is saved in it).
+;;
+;; The split pattern transforms the comparison into a save of one hard
+;; register and a comparison with the temporary.
+;;
+(define_split
+ [(set (cc0)
+ (compare (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "hard_reg_operand" "")))]
+ "TARGET_M6811
+ && reload_completed && !(Z_REG_P (operands[0]) || Z_REG_P (operands[1]))"
+ [(set (match_dup 2) (match_dup 1))
+ (set (cc0)
+ (compare (match_dup 0) (match_dup 2)))]
+ "operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);")
+
+(define_split
+ [(set (cc0)
+ (compare (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "hard_reg_operand" "")))]
+ "0 && TARGET_M6812
+ && reload_completed && !(Z_REG_P (operands[0]) || Z_REG_P (operands[1]))"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 1))
+ (set (cc0)
+ (compare (match_dup 0) (mem:HI (post_inc:HI (reg:HI SP_REGNUM)))))]
+ "")
+
+(define_insn "cmphi_1_hc12"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "tst_operand"
+ "d,?xy,xyd,?xy,d,m,!u,dxy,dxy")
+ (match_operand:HI 1 "general_operand"
+ "i,i,!u,m,m,dxy,dxy,?*d*A,!*w")))]
+ "TARGET_M6812"
+ "*
+{
+ if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
+ {
+ cc_status.flags |= CC_REVERSED;
+ return \"cp%1\\t%0\";
+ }
+ else if (SP_REG_P (operands[1]))
+ return \"sts\\t2,-sp\n\\tcp%0\\t2,sp+\";
+ else if (H_REG_P (operands[1]))
+ return \"psh%1\n\\tcp%0\\t2,sp+\";
+ else
+ return \"cp%0\\t%1\";
+}")
+
+(define_insn "cmphi_1_hc11"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "tst_operand"
+ "dx,y,xyd,?xy,d,m,m,dxy,dxy,?u*z,dxy,*z")
+ (match_operand:HI 1 "cmp_operand"
+ "i,i,!u,m,m,?xy,d,?*d*A,?u,dxy,!*w,i")))]
+ "TARGET_M6811"
+ "*
+{
+ if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
+ {
+ cc_status.flags |= CC_REVERSED;
+ return \"cp%1\\t%0\";
+ }
+ else if (H_REG_P (operands[1]))
+ return \"#\";
+ else
+ return \"cp%0\\t%1\";
+}")
+
+(define_insn_and_split "cmphi_z_used"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "tst_operand" "dxy,m")
+ (match_operand:HI 1 "cmp_operand" "mi,dxy")))
+ (use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
+ (use (reg:HI SOFT_Z_REGNUM))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
+ (set (match_dup 2) (match_dup 3))
+ (set (cc0) (compare (match_dup 0) (match_dup 1)))
+ (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
+
+;;
+;; 8-bit comparison with address register.
+;; There is no such comparison instruction, we have to temporarily switch
+;; the address register and the D register and do the comparison with D.
+;; The xgdx and xgdy instructions preserve the flags.
+;;
+(define_split
+ [(set (cc0)
+ (compare (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (match_operand:QI 1 "cmp_operand" "")))]
+ "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])
+ (set (cc0)
+ (compare (reg:QI D_REGNUM) (match_dup 1)))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])]
+ "operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));")
+
+(define_split
+ [(set (cc0)
+ (compare (match_operand:QI 0 "hard_reg_operand" "")
+ (match_operand:QI 1 "hard_reg_operand" "")))]
+ "reload_completed"
+ [(set (match_dup 3) (match_dup 4))
+ (set (cc0)
+ (compare (match_dup 0) (match_dup 2)))]
+ "operands[2] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ operands[3] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[4] = gen_rtx_REG (HImode, REGNO (operands[1]));")
+
+(define_insn "bitcmpqi"
+ [(set (cc0)
+ (compare (and:QI (match_operand:QI 0 "tst_operand" "d,d,d,m,!u")
+ (match_operand:QI 1 "cmp_operand" "im,*B,u,d,d"))
+ (const_int 0)))]
+ ""
+ "@
+ bitb\\t%b1
+ #
+ bitb\\t%b1
+ bitb\\t%b0
+ bitb\\t%b0")
+
+(define_split /* "bitcmpqi" */
+ [(set (cc0)
+ (compare (and:QI (match_operand:QI 0 "tst_operand" "")
+ (match_operand:QI 1 "hard_addr_reg_operand" ""))
+ (const_int 0)))]
+ "z_replacement_completed == 2"
+ [(set (match_dup 3) (match_dup 2))
+ (set (cc0) (and:QI (match_dup 0) (match_dup 4)))]
+ "operands[2] = gen_rtx_REG (HImode, REGNO (operands[1]));
+ operands[3] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[4] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);")
+
+(define_insn_and_split "bitcmpqi_z_used"
+ [(set (cc0)
+ (compare (and:QI (match_operand:QI 0 "tst_operand" "d,m")
+ (match_operand:QI 1 "cmp_operand" "m,d"))
+ (const_int 0)))
+ (use (match_operand:HI 2 "hard_reg_operand" "xy,xy"))
+ (use (reg:HI SOFT_Z_REGNUM))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
+ (set (match_dup 2) (match_dup 3))
+ (set (cc0) (and:QI (match_dup 0) (match_dup 1)))
+ (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
+
+(define_insn "bitcmphi"
+ [(set (cc0)
+ (compare (and:HI (match_operand:HI 0 "tst_operand" "d")
+ (match_operand:HI 1 "const_int_operand" "i"))
+ (const_int 0)))]
+ "(INTVAL (operands[1]) & 0x0ff) == 0
+ || (INTVAL (operands[1]) & 0x0ff00) == 0"
+ "*
+{
+ if ((INTVAL (operands[1]) & 0x0ff) == 0)
+ return \"bita\\t%h1\";
+ else
+ return \"bitb\\t%1\";
+}")
+
+(define_insn "bitcmpqi_12"
+ [(set (cc0)
+ (compare (zero_extract:HI (match_operand:HI 0 "tst_operand" "d")
+ (match_operand:HI 1 "const_int_operand" "i")
+ (match_operand:HI 2 "const_int_operand" "i"))
+ (const_int 0)))]
+ "(unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 8
+ || (((unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 16)
+ && (unsigned) INTVAL (operands[2]) >= 8)"
+ "*
+{
+ rtx ops[1];
+ int mask;
+ int startpos = INTVAL (operands[2]);
+ int bitsize = INTVAL (operands[1]);
+
+ if (startpos >= 8)
+ {
+ startpos -= 8;
+ mask = (1 << (startpos + bitsize)) - 1;
+ mask &= ~((1 << startpos) - 1);
+
+ ops[0] = GEN_INT (mask);
+ output_asm_insn (\"bita\\t%0\", ops);
+ }
+ else
+ {
+ mask = (1 << (startpos + bitsize)) - 1;
+ mask &= ~((1 << startpos) - 1);
+
+ ops[0] = GEN_INT (mask);
+ output_asm_insn (\"bitb\\t%0\", ops);
+ }
+ return \"\";
+}")
+
+(define_insn "cmpqi_1"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "tst_operand" "d,m,d,!u,*B,d*B")
+ (match_operand:QI 1 "cmp_operand" "im,d,!u,d,dim*A,*u")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || A_REG_P (operands[1]))
+ {
+ return \"#\";
+ }
+ else if (D_REG_P (operands[0]))
+ {
+ return \"cmpb\\t%b1\";
+ }
+ cc_status.flags |= CC_REVERSED;
+ return \"cmpb\\t%b0\";
+}")
+
+(define_insn_and_split "cmpqi_z_used"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "tst_operand" "dxy,m")
+ (match_operand:QI 1 "cmp_operand" "m,dxy")))
+ (use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
+ (use (reg:HI SOFT_Z_REGNUM))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
+ (set (match_dup 2) (match_dup 3))
+ (set (cc0) (compare (match_dup 0) (match_dup 1)))
+ (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
+
+;;--------------------------------------------------------------------
+;;- Move strict_low_part
+;;--------------------------------------------------------------------
+;;
+;; The (strict_low_part ...) patterns are replaced by normal (set) patterns.
+;; The replacement must be made at the very end because we loose the
+;; (strict_low_part ...) information. This is correct for our machine
+;; description but not for GCC optimization passes.
+;;
+(define_insn_and_split "movstrictsi"
+ [(set (strict_low_part (match_operand:SI 0 "non_push_operand" "+um,D,D"))
+ (match_operand:SI 1 "general_operand" "D,Dim,uD"))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (match_dup 0) (match_dup 1))]
+ "")
+
+(define_insn_and_split "movstricthi"
+ [(set (strict_low_part (match_operand:HI 0 "non_push_operand" "+um,dA,dA"))
+ (match_operand:HI 1 "general_operand" "dA,dAim,u"))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (match_dup 0) (match_dup 1))]
+ "")
+
+(define_insn_and_split "movstrictqi"
+ [(set (strict_low_part (match_operand:QI 0 "non_push_operand" "+mu,!dA"))
+ (match_operand:QI 1 "general_operand" "d,imudA"))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (match_dup 0) (match_dup 1))]
+ "")
+
+;;--------------------------------------------------------------------
+;;- 64-bit Move Operations.
+;; The movdi and movdf patterns are identical except for the mode.
+;; They are also very similar to those for movsi and movsf.
+;;
+;; For 68HC11, we need a scratch register (either D, X, Y)
+;; because there is no memory->memory moves. It must be defined with
+;; earlyclobber (&) so that it does not appear in the source or destination
+;; address. Providing patterns for movdi/movdf allows GCC to generate
+;; better code. [Until now, the scratch register is limited to D because
+;; otherwise we can run out of registers in the A_REGS class for reload].
+;;
+;; For 68HC12, the scratch register is not necessary. To use the same
+;; pattern and same split, we use the 'v' constraint. This tells the
+;; reload to use the _.tmp register (which is not used at all).
+;; The insn will be split in one or several memory moves (movw).
+;; [SCz: this does not work ?? So, I switched temporary to 'd' reg]
+;;--------------------------------------------------------------------
+(define_expand "movdi"
+ [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (match_operand:DI 1 "general_operand" ""))
+ (clobber (match_scratch:HI 2 ""))])]
+ ""
+ "
+ /* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_movdi_internal (operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+")
+
+;; Separate push from normal moves to avoid reloading problems
+;; The 'clr' is not able to push on 68HC11 so we really need a scratch.
+;; We can also accept more scratch registers.
+(define_insn_and_split "*pushdi_internal"
+ [(set (match_operand:DI 0 "push_operand" "=<,<,<,<")
+ (match_operand:DI 1 "general_operand" "i,U,m,!u"))
+ (clobber (match_scratch:HI 2 "=&dA,&d,&d,&dA"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_insn_and_split "movdi_internal"
+ [(set (match_operand:DI 0 "non_push_operand" "=m!u,U,!u,U,m,m,!u")
+ (match_operand:DI 1 "general_operand" "K,iU,iU,!u,mi,!u,!mu"))
+ (clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_expand "movdf"
+ [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (match_operand:DF 1 "general_operand" ""))
+ (clobber (match_scratch:HI 2 ""))])]
+ ""
+ "/* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_movdf_internal (operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+")
+
+;; See pushdi_internal
+(define_insn_and_split "*pushdf_internal"
+ [(set (match_operand:DF 0 "push_operand" "=<,<,<,<")
+ (match_operand:DF 1 "general_operand" "i,U,m,!u"))
+ (clobber (match_scratch:HI 2 "=&dA,&d,&d,&dA"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_insn_and_split "movdf_internal"
+ [(set (match_operand:DF 0 "non_push_operand" "=mu,U,m,!u,U,m,!u")
+ (match_operand:DF 1 "general_operand" "G,iU,mi,iU,!u,!u,!mu"))
+ (clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+;;--------------------------------------------------------------------
+;;- 32-bit Move Operations.
+;; The movsi and movsf patterns are identical except for the mode.
+;; When we move to/from a hard register (d+x), we don't need a scratch.
+;; Otherwise, a scratch register is used as intermediate register for
+;; the move. The '&' constraint is necessary to make sure the reload
+;; pass does not give us a register that dies in the insn and is used
+;; for input/output operands.
+;;--------------------------------------------------------------------
+(define_expand "movsi"
+ [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" ""))
+ (clobber (match_scratch:HI 2 ""))])]
+ ""
+ "/* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_movsi_internal (operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+")
+
+(define_insn_and_split "*pushsi_internal"
+ [(set (match_operand:SI 0 "push_operand" "=<,<,<,<,<")
+ (match_operand:SI 1 "general_operand" "!D,i,U,m,!u"))
+ (clobber (match_scratch:HI 2 "=X,&dA,&d,&d,&dA"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_insn_and_split "movsi_internal"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=mu,mu,?D,m,?D,?u,?u,!u,D")
+ (match_operand:SI 1 "general_operand" "K,imu,im,?D,!u,?D,mi,!u,!D"))
+ (clobber (match_scratch:HI 2 "=X,&d,X,X,X,X,&d,&d,X"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_expand "movsf"
+ [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (match_operand:SF 1 "general_operand" ""))
+ (clobber (match_scratch:HI 2 ""))])]
+ ""
+ "/* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_movsf_internal (operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+")
+
+(define_insn_and_split "*pushsf_internal"
+ [(set (match_operand:SF 0 "push_operand" "=<,<,<,<,<")
+ (match_operand:SF 1 "general_operand" "!D,i,U,m,!u"))
+ (clobber (match_scratch:HI 2 "=X,&dA,&d,&d,&dA"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+(define_insn_and_split "movsf_internal"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=m!u,m,D,m,D,!u,!u,!u,D")
+ (match_operand:SF 1 "general_operand" "G,im,im,D,!u,D,mi,!u,!D"))
+ (clobber (match_scratch:HI 2 "=X,&d,X,X,X,X,&d,&d,X"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (operands[0], operands[1], operands[2]);
+ DONE;")
+
+
+;;--------------------------------------------------------------------
+;;- 16-bit Move Operations.
+;; We don't need a scratch register.
+;;--------------------------------------------------------------------
+
+(define_insn "*movhi2_push"
+ [(set (match_operand:HI 0 "push_operand" "=<,<,<")
+ (match_operand:HI 1 "general_operand" "xy,?d,!z"))]
+ "TARGET_M6811 && !TARGET_M6812"
+ "*
+{
+ cc_status = cc_prev_status;
+ if (D_REG_P (operands[1]))
+ {
+ output_asm_insn (\"pshb\", operands);
+ return \"psha\";
+ }
+ else if (X_REG_P (operands[1]))
+ {
+ return \"pshx\";
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ return \"pshy\";
+ }
+ fatal_insn (\"Invalid register in the instruction\", insn);
+}")
+
+(define_insn "*movhi2_pop"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=xy,d")
+ (match_operand:HI 1 "pop_operand" ">,>"))]
+ "TARGET_M6811"
+ "*
+{
+ cc_status = cc_prev_status;
+ if (D_REG_P (operands[0]))
+ {
+ output_asm_insn (\"pula\", operands);
+ return \"pulb\";
+ }
+ else if (X_REG_P (operands[0]))
+ {
+ return \"pulx\";
+ }
+ else if (Y_REG_P (operands[0]))
+ {
+ return \"puly\";
+ }
+ fatal_insn (\"Invalid register in the instruction\", insn);
+}")
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (reload_in_progress)
+ {
+ if (m68hc11_reload_operands (operands))
+ {
+ DONE;
+ }
+ }
+ if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
+ {
+ if (GET_CODE (operands[0]) == MEM &&
+ (GET_CODE (operands[1]) == MEM
+ || GET_CODE (operands[1]) == CONST_INT))
+ {
+ operands[1] = force_reg (HImode, operands[1]);
+ }
+ else if (IS_STACK_PUSH (operands[0])
+ && GET_CODE (operands[1]) != REG)
+ {
+ operands[1] = force_reg (HImode, operands[1]);
+ }
+ }
+ /* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+}")
+
+(define_insn "*movhi_68hc12"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=U,dAw,dAw,m,U,U,m,!u")
+ (match_operand:HI 1 "general_operand" "U,dAwim,!u,K,dAwi,!u,dAw,riU"))]
+ "TARGET_M6812"
+ "*
+{
+ m68hc11_gen_movhi (insn, operands);
+ return \"\";
+}")
+
+(define_insn "movhi_const0"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,A,um")
+ (const_int 0))]
+ "TARGET_M6811"
+ "@
+ clra\\n\\tclrb
+ ld%0\\t#0
+ clr\\t%b0\\n\\tclr\\t%h0")
+
+(define_insn "*movhi_m68hc11"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=dAw,!u,m,m,dAw,!*u")
+ (match_operand:HI 1 "general_operand" "dAwim,dAw,dA,?Aw,!*u,dAw"))]
+ "TARGET_M6811"
+ "*
+{
+ m68hc11_gen_movhi (insn, operands);
+ return \"\";
+}")
+
+;;--------------------------------------------------------------------
+;;- 8-bit Move Operations.
+;; We don't need a scratch register.
+;;--------------------------------------------------------------------
+;;
+;; The *a alternative also clears the high part of the register.
+;; This should be ok since this is not the (strict_low_part) set.
+;;
+(define_insn "movqi_const0"
+ [(set (match_operand:QI 0 "non_push_operand" "=d,m,!u,*A,!*q")
+ (const_int 0))]
+ ""
+ "@
+ clrb
+ clr\\t%b0
+ clr\\t%b0
+ ld%0\\t#0
+ clr%0")
+
+;;
+;; 8-bit operations on address registers.
+;;
+;; Switch temporary to the D register and load the value in B.
+;; This is possible as long as the address register does not
+;; appear in the source operand.
+;;
+(define_split
+ [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ "z_replacement_completed == 2
+ && !reg_mentioned_p (operands[0], operands[1])
+ && !(D_REG_P (operands[1]) || Q_REG_P (operands[1]))"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
+ (set (match_dup 2) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (match_dup 1))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 2))
+ (set (match_dup 2) (reg:HI D_REGNUM))])]
+ "operands[2] = gen_rtx_REG (HImode, REGNO (operands[0]));")
+
+;;
+;; 8-bit operations on address registers.
+;;
+(define_split
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (match_operand:QI 1 "hard_addr_reg_operand" ""))]
+ "z_replacement_completed == 2
+ && !reg_mentioned_p (operands[1], operands[0])
+ && !(D_REG_P (operands[0]) || Q_REG_P (operands[0]))"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
+ (set (match_dup 2) (reg:HI D_REGNUM))])
+ (set (match_dup 0) (reg:QI D_REGNUM))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 2))
+ (set (match_dup 2) (reg:HI D_REGNUM))])]
+ "operands[2] = gen_rtx_REG (HImode, REGNO (operands[1]));")
+
+(define_insn "*movqi2_push"
+ [(set (match_operand:QI 0 "push_operand" "=<,<")
+ (match_operand:QI 1 "general_operand" "d,!*A"))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[1]))
+ return \"#\";
+
+ cc_status = cc_prev_status;
+ return \"pshb\";
+}")
+
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (reload_in_progress)
+ {
+ if (m68hc11_reload_operands (operands))
+ {
+ DONE;
+ }
+ }
+ if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
+ {
+ if (GET_CODE (operands[0]) == MEM
+ && (GET_CODE (operands[1]) == MEM
+ || GET_CODE (operands[1]) == CONST_INT))
+ {
+ operands[1] = force_reg (QImode, operands[1]);
+ }
+ else if (IS_STACK_PUSH (operands[0])
+ && GET_CODE (operands[1]) != REG)
+ {
+ operands[1] = force_reg (QImode, operands[1]);
+ }
+ }
+ /* For push/pop, emit a REG_INC note to make sure the reload
+ inheritance and reload CSE pass notice the change of the stack
+ pointer. */
+ if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
+ {
+ rtx insn;
+
+ insn = emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
+ stack_pointer_rtx,
+ REG_NOTES (insn));
+ DONE;
+ }
+}")
+
+(define_insn "*movqi_68hc12"
+ [(set (match_operand:QI 0 "nonimmediate_operand"
+ "=U,d*AU*q,d*A*qU,d*A*q,m,?*u,m")
+ (match_operand:QI 1 "general_operand"
+ "U,*ri*q,U,m,d*q,*ri*qU,!*A"))]
+ "TARGET_M6812"
+ "*
+{
+ m68hc11_gen_movqi (insn, operands);
+ return \"\";
+}")
+
+(define_insn "*movqi_m68hc11"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d*A*q,m,m,d*A*q,*u")
+ (match_operand:QI 1 "general_operand" "d*Aim*q,d*q,!*A,*u,d*A*q"))]
+ "TARGET_M6811"
+ "*
+{
+ m68hc11_gen_movqi (insn, operands);
+ return \"\";
+}")
+
+;;--------------------------------------------------------------------
+;;- Swap registers
+;;--------------------------------------------------------------------
+;; Swapping registers is used for split patterns.
+(define_insn "swap_areg"
+ [(set (match_operand:HI 0 "hard_reg_operand" "=d,A")
+ (match_operand:HI 1 "hard_reg_operand" "=A,d"))
+ (set (match_dup 1) (match_dup 0))]
+ ""
+ "*
+{
+ m68hc11_output_swap (insn, operands);
+ return \"\";
+}")
+
+;;--------------------------------------------------------------------
+;;- Truncation insns.
+;;--------------------------------------------------------------------
+;;
+;; Truncation are not necessary because GCC knows how to truncate,
+;; specially when values lie in consecutive registers.
+;;
+
+(define_expand "floatunssisf2"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (unsigned_float:SF (match_operand:SI 1 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"__floatunsisf\", UNSIGNED_FLOAT,
+ SFmode, SImode, 2, operands);
+ DONE;")
+
+(define_expand "floatunssidf2"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (unsigned_float:DF (match_operand:SI 1 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"__floatunsidf\", UNSIGNED_FLOAT,
+ DFmode, SImode, 2, operands);
+ DONE;")
+
+;;--------------------------------------------------------------------
+;;- Zero extension insns.
+;;--------------------------------------------------------------------
+
+;;
+;; 64-bit extend. The insn will be split into 16-bit instructions just
+;; before the final pass. We need a scratch register for the split.
+;; The final value can be generated on the stack directly. This is more
+;; efficient and useful for conversions made during parameter passing rules.
+;;
+(define_insn "zero_extendqidi2"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=m,!u,m,!u")
+ (zero_extend:DI
+ (match_operand:QI 1 "nonimmediate_operand" "m,dmu,*B,*B")))
+ (clobber (match_scratch:HI 2 "=&d,&dB,&d,&dB"))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "push_operand" "")
+ (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:HI 2 "=&dB"))]
+ "z_replacement_completed == 2"
+ [(const_int 0)]
+ "
+{
+ rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
+ rtx push = m68hc11_gen_lowpart (HImode, low);
+ rtx src = operands[1];
+
+ /* Source operand must be in a hard register. */
+ if (!H_REG_P (src))
+ {
+ src = gen_rtx_REG (QImode, REGNO (operands[2]));
+ emit_move_insn (src, operands[1]);
+ }
+
+ /* Source is in D, we can push B then one word of 0 and we do
+ a correction on the stack pointer. */
+ if (D_REG_P (src))
+ {
+ emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
+ emit_move_insn (operands[2], const0_rtx);
+ if (D_REG_P (operands[2]))
+ {
+ emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
+ }
+ else
+ {
+ emit_move_insn (push, operands[2]);
+ emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ const1_rtx));
+ }
+ }
+ else
+ {
+ /* Source is in X or Y. It's better to push the 16-bit register
+ and then to some stack adjustment. */
+ src = gen_rtx_REG (HImode, REGNO (src));
+ emit_move_insn (push, src);
+ emit_move_insn (operands[2], const0_rtx);
+ emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ const1_rtx));
+ emit_move_insn (push, operands[2]);
+ emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ const1_rtx));
+ }
+ emit_move_insn (push, operands[2]);
+ emit_move_insn (push, operands[2]);
+ emit_move_insn (push, operands[2]);
+ DONE;
+}")
+
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:HI 2 "=&dB"))]
+ "z_replacement_completed == 2"
+ [(const_int 0)]
+ "
+{
+ rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
+ rtx low2 = m68hc11_gen_lowpart (HImode, low);
+ rtx src = operands[1];
+
+ /* Source operand must be in a hard register. */
+ if (!H_REG_P (src))
+ {
+ src = gen_rtx_REG (QImode, REGNO (operands[2]));
+ emit_move_insn (src, operands[1]);
+ }
+
+ emit_move_insn (m68hc11_gen_lowpart (QImode, low2), src);
+ emit_move_insn (operands[2], const0_rtx);
+ src = gen_rtx_REG (QImode, REGNO (operands[2]));
+ emit_move_insn (m68hc11_gen_highpart (QImode, low2), src);
+
+ emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
+ low = m68hc11_gen_highpart (SImode, operands[0]);
+ emit_move_insn (m68hc11_gen_lowpart (HImode, low), operands[2]);
+ emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
+ DONE;
+}")
+
+(define_insn "zero_extendhidi2"
+ [(set (match_operand:DI 0 "non_push_operand" "=m,m,m,m,!u,!u")
+ (zero_extend:DI
+ (match_operand:HI 1 "nonimmediate_operand" "m,d,A,!u,dmA,!u")))
+ (clobber (match_scratch:HI 2 "=&d,&B,&d,&dB,&dB,&dB"))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "non_push_operand" "")
+ (zero_extend:DI
+ (match_operand:HI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:HI 2 ""))]
+ "z_replacement_completed == 2"
+ [(const_int 0)]
+ "
+{
+ rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
+ rtx high = m68hc11_gen_highpart (SImode, operands[0]);
+ rtx src = operands[1];
+
+ /* Make sure the source is in a hard register. */
+ if (!H_REG_P (src))
+ {
+ src = operands[2];
+ emit_move_insn (src, operands[1]);
+ }
+
+ /* Move the low part first for the push. */
+ emit_move_insn (m68hc11_gen_lowpart (HImode, low), src);
+
+ /* Now, use the scratch register to fill in the zeros. */
+ emit_move_insn (operands[2], const0_rtx);
+ emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
+ emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
+ emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
+ DONE;
+}")
+
+(define_insn "zero_extendsidi2"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=m,m,!u,!u")
+ (zero_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand" "m,Du,m,Du")))
+ (clobber (match_scratch:HI 2 "=d,d,d,d"))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (zero_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:HI 2 ""))]
+ "z_replacement_completed == 2"
+ [(const_int 0)]
+ "
+{
+ rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
+ rtx high = m68hc11_gen_highpart (SImode, operands[0]);
+
+ /* Move the low part first so that this is ok for a push. */
+ m68hc11_split_move (low, operands[1], operands[2]);
+
+ /* Use the scratch register to clear the high part of the destination. */
+ emit_move_insn (operands[2], const0_rtx);
+ emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
+ emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
+ DONE;
+}")
+
+;;
+;; For 16->32bit unsigned extension, we don't allow generation on the stack
+;; because it's less efficient.
+;;
+(define_insn "zero_extendhisi2"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,m,u,m,m,!u,!u")
+ (zero_extend:SI
+ (match_operand:HI 1 "nonimmediate_operand" "dAmu,dA,dA,m,!u,m,!u")))
+ (clobber (match_scratch:HI 2 "=X,X,X,&d,&dB,&dB,&dB"))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:SI 0 "non_push_operand" "")
+ (zero_extend:SI
+ (match_operand:HI 1 "nonimmediate_operand" "")))
+ (clobber (match_scratch:HI 2 ""))]
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx src = operands[1];
+
+ if (!H_REG_P (src) && !H_REG_P (operands[0]))
+ {
+ src = operands[2];
+ emit_move_insn (src, operands[1]);
+ }
+ emit_move_insn (m68hc11_gen_lowpart (HImode, operands[0]), src);
+ emit_move_insn (m68hc11_gen_highpart (HImode, operands[0]), const0_rtx);
+ DONE;
+}")
+
+(define_insn "zero_extendqisi2"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,D,m,m,u")
+ (zero_extend:SI
+ (match_operand:QI 1 "nonimmediate_operand" "dmu,xy,d,xy,dxy")))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:SI 0 "non_push_operand" "")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ "reload_completed && !X_REG_P (operands[0])"
+ [(set (match_dup 2) (zero_extend:HI (match_dup 1)))
+ (set (match_dup 3) (const_int 0))]
+ "
+ operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
+ operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")
+
+(define_split
+ [(set (match_operand:SI 0 "hard_reg_operand" "")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ "z_replacement_completed == 2 && X_REG_P (operands[0])"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (const_int 0))
+ (set (match_dup 5) (zero_extend:HI (match_dup 6)))]
+ "
+ if (X_REG_P (operands[1]))
+ {
+ emit_insn (gen_swap_areg (gen_rtx_REG (HImode, HARD_D_REGNUM),
+ gen_rtx_REG (HImode, HARD_X_REGNUM)));
+ emit_insn (gen_zero_extendqihi2 (gen_rtx_REG (HImode, HARD_D_REGNUM),
+ gen_rtx_REG (QImode, HARD_D_REGNUM)));
+ emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM),
+ const0_rtx);
+ DONE;
+ }
+
+ if (reg_mentioned_p (gen_rtx_REG (HImode, HARD_X_REGNUM), operands[1]))
+ {
+ emit_insn (gen_zero_extendqihi2 (m68hc11_gen_lowpart (HImode,
+ operands[0]),
+ operands[1]));
+ emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM), const0_rtx);
+ DONE;
+ }
+ operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
+ operands[5] = m68hc11_gen_lowpart (HImode, operands[0]);
+ if (A_REG_P (operands[1]))
+ {
+ operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[3] = gen_rtx_REG (HImode, REGNO (operands[1]));
+ operands[6] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ }
+ else
+ {
+ operands[5] = operands[2] =
+ operands[3] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ operands[6] = operands[1];
+ }
+")
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "non_push_operand" "=dm,d,*A,!*u,d,m,!*u")
+ (zero_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "d,*A,d*Am,d,!um,*A,*A")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (H_REG_P (operands[0]))
+ {
+ output_asm_insn (\"clra\", operands);
+ if (operands[0] != operands[1]
+ && !(D_REG_P (operands[0]) && D_REG_P (operands[1])))
+ {
+ if (X_REG_P (operands[1])
+ || (D_REG_P (operands[1]) && X_REG_P (operands[0])))
+ {
+ output_asm_insn (\"stx\\t%t1\", operands);
+ output_asm_insn (\"ldab\\t%T0\", operands);
+ }
+ else if (Y_REG_P (operands[1])
+ || (D_REG_P (operands[1]) && Y_REG_P (operands[0])))
+ {
+ output_asm_insn (\"sty\\t%t1\", operands);
+ output_asm_insn (\"ldab\\t%T0\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ }
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ }
+ else
+ {
+ /* Status refers to the clra insn. Status is ok for others
+ * since we have loaded the value in B.
+ */
+ CC_STATUS_INIT;
+ }
+ return \"\";
+ }
+
+ if (A_REG_P (operands[1]))
+ {
+ output_asm_insn (\"st%1\\t%0\", operands);
+ output_asm_insn (\"clr\\t%h0\", operands);
+ CC_STATUS_INIT;
+ }
+ else
+ {
+ output_asm_insn (\"clr\\t%h0\", operands);
+ output_asm_insn (\"stab\\t%b0\", operands);
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ }
+
+ return \"\";
+}")
+
+
+;;--------------------------------------------------------------------
+;;- Sign extension insns.
+;;--------------------------------------------------------------------
+
+(define_insn "extendqisi2"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,u")
+ (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dmux,d,d")))]
+ ""
+ "*
+{
+ rtx ops[3];
+ int need_tst = 0;
+
+ /* The 68HC12 has a sign-extension instruction. Use it when the
+ destination is the register (X,D). First sign-extend the low
+ part and fill X with the sign-extension of the high part. */
+ if (TARGET_M6812 && X_REG_P (operands[0]))
+ {
+ if (!D_REG_P (operands[1]))
+ {
+ ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movqi (insn, ops);
+ }
+ return \"sex\\tb,d\\n\\tsex\\ta,x\";
+ }
+
+ ops[2] = gen_label_rtx ();
+
+ if (X_REG_P (operands[1]))
+ {
+ output_asm_insn (\"xgdx\", operands);
+ need_tst = 1;
+ }
+ else if (X_REG_P (operands[0]))
+ {
+ /* X can be used as an indexed addressing in the source.
+ Get the value before clearing it. */
+ if (reg_mentioned_p (ix_reg, operands[1]))
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ need_tst = 1;
+ }
+ output_asm_insn (\"ldx\\t#0\", operands);
+ }
+
+ output_asm_insn (\"clra\", operands);
+ if (!X_REG_P (operands[0]))
+ {
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
+ ops[1] = m68hc11_gen_lowpart (QImode, ops[0]);
+
+ if (IS_STACK_PUSH (operands[0]))
+ {
+ output_asm_insn (\"pshb\", ops);
+ output_asm_insn (\"tstb\", ops);
+ }
+ else
+ {
+ output_asm_insn (\"stab\\t%b1\", ops);
+ }
+ }
+ else if (D_REG_P (operands[1]) || need_tst)
+ {
+ output_asm_insn (\"tstb\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ }
+ output_asm_insn (\"bpl\\t%l2\", ops);
+ output_asm_insn (\"deca\", operands);
+ if (X_REG_P (operands[0]))
+ output_asm_insn (\"dex\", operands);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
+
+ if (!X_REG_P (operands[0]))
+ {
+ if (IS_STACK_PUSH (operands[0]))
+ {
+ output_asm_insn (\"psha\", ops);
+ output_asm_insn (\"psha\", ops);
+ output_asm_insn (\"psha\", ops);
+ }
+ else
+ {
+ output_asm_insn (\"staa\\t%h0\", ops);
+
+ ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
+ if (dead_register_here (insn, d_reg))
+ {
+ output_asm_insn (\"tab\", ops);
+ output_asm_insn (\"std\\t%0\", ops);
+ }
+ else
+ {
+ output_asm_insn (\"staa\\t%b0\", ops);
+ output_asm_insn (\"staa\\t%h0\", ops);
+ }
+ }
+ }
+
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "non_push_operand" "=d,*x*ym,u")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "dum,0,0")))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ ops[0] = gen_label_rtx ();
+ if (D_REG_P (operands[0]))
+ {
+ if (TARGET_M6812)
+ {
+ if (!D_REG_P (operands[1]))
+ {
+ ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movqi (insn, ops);
+ }
+ return \"sex\\tb,d\";
+ }
+ output_asm_insn (\"clra\", operands);
+ if (H_REG_P (operands[1]))
+ {
+ output_asm_insn (\"tstb\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ }
+ output_asm_insn (\"bpl\\t%l0\", ops);
+ output_asm_insn (\"deca\", operands);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+ }
+ else
+ {
+ output_asm_insn (\"clr\\t%h0\", operands);
+ if (m68hc11_register_indirect_p (operands[1], HImode))
+ {
+ ops[1] = operands[1];
+ output_asm_insn (\"brclr\\t%b1 #0x80 %l0\", ops);
+ CC_STATUS_INIT;
+ }
+ else
+ {
+ output_asm_insn (\"tst\\t%b1\", operands);
+ output_asm_insn (\"bpl\\t%l0\", ops);
+ }
+ output_asm_insn (\"dec\\t%h0\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+ }
+
+ return \"\";
+}")
+
+;;
+;; Split the special case where the source of the sign extend is
+;; either Y or Z. In that case, we can't move the source in the D
+;; register directly. The movhi pattern handles this move by using
+;; a temporary scratch memory location.
+;;
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
+ "reload_completed && (Y_REG_P (operands[1]) || Z_REG_P (operands[1]))"
+ [(set (reg:HI D_REGNUM) (match_dup 1))
+ (set (match_dup 0) (sign_extend:SI (reg:HI D_REGNUM)))]
+ "")
+
+(define_insn "extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D")
+ (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "m,!r,dA")))]
+ ""
+ "*
+{
+ rtx ops[2];
+ int x_reg_used;
+
+ if (Y_REG_P (operands[1]))
+ return \"#\";
+
+ if (X_REG_P (operands[1]))
+ {
+ output_asm_insn (\"xgdx\", operands);
+ x_reg_used = 1;
+ }
+ else
+ {
+ /* X can be used as an indexed addressing in the source.
+ Get the value before clearing it. */
+ x_reg_used = reg_mentioned_p (ix_reg, operands[1]);
+ if (x_reg_used)
+ {
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ }
+ }
+
+ CC_STATUS_INIT;
+ if (TARGET_M6812 && 0)
+ {
+ /* This sequence of code is larger than the one for 68HC11.
+ Don't use it; keep it for documentation. */
+ if (!D_REG_P (operands[1]) && !x_reg_used)
+ {
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ }
+ output_asm_insn (\"sex\\ta,x\", operands);
+ output_asm_insn (\"xgdx\", operands);
+ output_asm_insn (\"sex\\ta,d\", operands);
+ return \"xgdx\";
+ }
+
+ output_asm_insn (\"ldx\\t#0\", operands);
+ if (D_REG_P (operands[1]) || x_reg_used)
+ {
+ output_asm_insn (\"tsta\", operands);
+ }
+ else
+ {
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ }
+
+ ops[0] = gen_label_rtx ();
+ output_asm_insn (\"bpl\\t%l0\", ops);
+ output_asm_insn (\"dex\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
+
+ return \"\";
+}")
+
+
+;;--------------------------------------------------------------------
+;;- Min and Max instructions (68HC12).
+;;--------------------------------------------------------------------
+(define_insn "uminqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
+ (umin:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "m,d")))]
+ "TARGET_M6812 && TARGET_MIN_MAX"
+ "*
+{
+ /* Flags are set according to (sub:QI (operand 1) (operand2)).
+ The mina/minm use A as the source or destination. This is the
+ high part of D. There is no way to express that in the pattern
+ so we must use 'exg a,b' to put the operand in the good register. */
+ CC_STATUS_INIT;
+ if (D_REG_P (operands[0]))
+ {
+ return \"exg\\ta,b\\n\\tmina\\t%2\\n\\texg\\ta,b\";
+ }
+ else
+ {
+ return \"exg\\ta,b\\n\\tminm\\t%0\\n\\texg\\ta,b\";
+ }
+}")
+
+(define_insn "umaxqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
+ (umax:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "m,d")))]
+ "TARGET_M6812 && TARGET_MIN_MAX"
+ "*
+{
+ /* Flags are set according to (sub:QI (operand 1) (operand2)).
+ The maxa/maxm use A as the source or destination. This is the
+ high part of D. There is no way to express that in the pattern
+ so we must use 'exg a,b' to put the operand in the good register. */
+ CC_STATUS_INIT;
+ if (D_REG_P (operands[0]))
+ {
+ return \"exg\\ta,b\\n\\tmaxa\\t%2\\n\\texg\\ta,b\";
+ }
+ else
+ {
+ return \"exg\\ta,b\\n\\tmaxm\\t%0\\n\\texg\\ta,b\";
+ }
+}")
+
+(define_insn "uminhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
+ (umin:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "m,d")))]
+ "TARGET_M6812 && TARGET_MIN_MAX"
+ "*
+{
+ /* Flags are set according to (sub:HI (operand 1) (operand2)). */
+ CC_STATUS_INIT;
+ if (D_REG_P (operands[0]))
+ {
+ return \"emind\\t%2\";
+ }
+ else
+ {
+ return \"eminm\\t%0\";
+ }
+}")
+
+(define_insn "umaxhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
+ (umax:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "m,d")))]
+ "TARGET_M6812 && TARGET_MIN_MAX"
+ "*
+{
+ /* Flags are set according to (sub:HI (operand 1) (operand2)). */
+ CC_STATUS_INIT;
+ if (D_REG_P (operands[0]))
+ {
+ return \"emaxd\\t%2\";
+ }
+ else
+ {
+ return \"emaxm\\t%0\";
+ }
+}")
+
+
+;;--------------------------------------------------------------------
+;;- Add instructions.
+;;--------------------------------------------------------------------
+;; 64-bit: Use a library call because what GCC generates is huge.
+;;
+(define_expand "adddi3"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (plus:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:DI 2 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"___adddi3\", PLUS, DImode, DImode, 3, operands);
+ DONE;")
+
+;;
+;; - 32-bit Add.
+;;
+(define_expand "addsi3"
+ [(parallel [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 ""))])]
+ ""
+ "")
+
+(define_insn "*addsi3_zero_extendhi"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D,D")
+ (plus:SI (zero_extend:SI
+ (match_operand:HI 1 "general_operand" "dxi,!u,mdxi,!u"))
+ (match_operand:SI 2 "general_operand" "mi,mi,D?u,!Du")))
+ (clobber (match_scratch:HI 3 "=X,X,X,X"))]
+ ""
+ "*
+{
+ rtx ops[3];
+
+ if (X_REG_P (operands[2]))
+ {
+ ops[0] = operands[1];
+ }
+ else
+ {
+ if (X_REG_P (operands[1]))
+ {
+ output_asm_insn (\"xgdx\", ops);
+ }
+ else if (!D_REG_P (operands[1]))
+ {
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ }
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
+ ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
+ }
+ ops[2] = gen_label_rtx ();
+
+ /* ldx preserves the carry, propagate it by incrementing X directly. */
+ output_asm_insn (\"addd\\t%0\", ops);
+ if (!X_REG_P (operands[2]))
+ output_asm_insn (\"ldx\\t%1\", ops);
+
+ output_asm_insn (\"bcc\\t%l2\", ops);
+ output_asm_insn (\"inx\", ops);
+
+ CC_STATUS_INIT;
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
+ return \"\";
+}")
+
+
+(define_split /* "*addsi3_zero_extendqi" */
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (zero_extend:SI
+ (match_operand:QI 1 "general_operand" ""))
+ (match_operand:SI 2 "memory_operand" "")))
+ (clobber (match_scratch:HI 3 "=X,X"))]
+ "reload_completed"
+ [(set (reg:HI D_REGNUM) (zero_extend:HI (match_dup 1)))
+ (parallel [(set (match_dup 0)
+ (plus:SI (zero_extend:SI (reg:HI D_REGNUM)) (match_dup 2)))
+ (clobber (match_dup 3))])]
+ "")
+
+(define_insn "*addsi3_zero_extendqi"
+ [(set (match_operand:SI 0 "register_operand" "=D,D")
+ (plus:SI (zero_extend:SI
+ (match_operand:QI 1 "general_operand" "dAmi,!dAmiu"))
+ (match_operand:SI 2 "general_operand" "miD,!muiD")))
+ (clobber (match_scratch:HI 3 "=X,X"))]
+ ""
+ "*
+{
+ rtx ops[4];
+
+ if (GET_CODE (operands[2]) == MEM)
+ return \"#\";
+
+ if (X_REG_P (operands[2]))
+ {
+ if (H_REG_P (operands[1]))
+ {
+ ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ ops[1] = gen_rtx_REG (HImode, REGNO (operands[1]));
+ m68hc11_gen_movhi (insn, ops);
+ }
+ else
+ {
+ ops[0] = operands[1];
+ }
+ ops[1] = const0_rtx;
+ }
+ else
+ {
+ if (X_REG_P (operands[1]))
+ {
+ output_asm_insn (\"xgdx\", ops);
+ }
+ else if (!D_REG_P (operands[1]))
+ {
+ ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ ops[1] = operands[1];
+ m68hc11_gen_movqi (insn, ops);
+ }
+
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
+ ops[1] = ops[0];
+ ops[2] = m68hc11_gen_highpart (HImode, operands[2]);
+ output_asm_insn (\"clra\", ops);
+ }
+
+ /* ldx preserves the carry, propagate it by incrementing X directly. */
+ output_asm_insn (\"addb\\t%b0\", ops);
+ output_asm_insn (\"adca\\t%h1\", ops);
+ if (!X_REG_P (operands[2]))
+ output_asm_insn (\"ldx\\t%2\", ops);
+
+ /* If the above adca was adding some constant, we don't need to propagate
+ the carry unless the constant was 0xff. */
+ if (X_REG_P (operands[2])
+ || GET_CODE (ops[1]) != CONST_INT
+ || ((INTVAL (ops[1]) & 0x0ff00) == 0x0ff00))
+ {
+ ops[3] = gen_label_rtx ();
+
+ output_asm_insn (\"bcc\\t%l3\", ops);
+ output_asm_insn (\"inx\", ops);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[3]));
+ }
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+(define_insn "*addsi3"
+ [(set (match_operand:SI 0 "non_push_operand" "=o,D,!u,?D,D")
+ (plus:SI (match_operand:SI 1 "non_push_operand" "%0,0,0,0,0")
+ (match_operand:SI 2 "general_operand" "ML,i,ML,?D,?oiu")))
+ (clobber (match_scratch:HI 3 "=d,X,d,X,X"))]
+ ""
+ "*
+{
+ rtx ops[3];
+ const char* add_insn;
+ const char* inc_insn;
+ const char* incb_mem;
+ const char* inch_mem;
+ HOST_WIDE_INT val;
+
+ if (which_alternative > 2)
+ {
+ return \"#\";
+ }
+
+ val = INTVAL (operands[2]);
+ if ((val & 0x0ffffL) == 0)
+ {
+ if (!H_REG_P (operands[0]))
+ {
+ ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
+ ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
+ output_asm_insn (\"ldd\\t%0\", ops);
+ output_asm_insn (\"addd\\t%1\", ops);
+ output_asm_insn (\"std\\t%0\", ops);
+ return \"\";
+ }
+ else if (val == 1)
+ {
+ return \"inx\";
+ }
+ else
+ {
+ return \"#\";
+ }
+ }
+ if ((val & 0xffff0000L) != 0 && (val & 0xffff0000L) != 0xffff0000L)
+ {
+ return \"#\";
+ }
+
+ if (val >= 0)
+ {
+ ops[1] = operands[2];
+ add_insn = \"addd\\t%1\";
+ inc_insn = \"inx\\t\";
+ incb_mem = \"inc\\t%b1\";
+ inch_mem = \"inc\\t%h1\";
+ }
+ else
+ {
+ ops[1] = GEN_INT (- val);
+ add_insn = \"subd\\t%1\";
+ inc_insn = \"dex\";
+ incb_mem = \"dec\\t%b1\";
+ inch_mem = \"dec\\t%h1\";
+ }
+
+ ops[2] = gen_label_rtx ();
+ if (!H_REG_P (operands[0]))
+ {
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
+ output_asm_insn (\"ldd\\t%0\", ops);
+ }
+ output_asm_insn (add_insn, ops);
+ if (!H_REG_P (operands[0]))
+ {
+ output_asm_insn (\"std\\t%0\", ops);
+ }
+ output_asm_insn (\"bcc\\t%l2\", ops);
+ if (H_REG_P (operands[0]))
+ {
+ output_asm_insn (inc_insn, ops);
+ }
+ else
+ {
+ ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
+ ops[1] = ops[0];
+ if (INTVAL (operands[2]) < 0)
+ {
+ output_asm_insn (\"ldd\\t%1\", ops);
+ output_asm_insn (\"addd\\t#-1\", ops);
+ output_asm_insn (\"std\\t%1\", ops);
+ }
+ else
+ {
+ output_asm_insn (incb_mem, ops);
+ output_asm_insn (\"bne\\t%l2\", ops);
+ output_asm_insn (inch_mem, ops);
+ }
+ }
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
+
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))
+ (clobber (match_scratch:HI 3 ""))]
+ "reload_completed && z_replacement_completed == 2
+ && ((INTVAL (operands[2]) & 0x0FFFF) == 0)"
+ [(set (match_dup 5) (match_dup 6))
+ (set (reg:HI 0) (plus:HI (reg:HI 0) (match_dup 4)))
+ (set (match_dup 6) (match_dup 5))]
+ "operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
+ if (X_REG_P (operands[0]))
+ {
+ operands[5] = operands[6] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ }
+ else
+ {
+ operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
+ operands[5] = operands[3];
+ }
+ ")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "reload_completed && z_replacement_completed == 2
+ && (GET_CODE (operands[2]) != CONST_INT ||
+ (!(INTVAL (operands[2]) >= -65536 && INTVAL (operands[2]) <= 65535)))"
+ [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
+ (parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
+ (set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])
+ (set (reg:QI B_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
+ (set (reg:QI A_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
+ (parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
+ (set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])]
+ "operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
+ operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
+
+;;
+;; Instruction generated to propagate the carry of a 16-bit add
+;; to the upper 16-bit part (in register X).
+;;
+(define_insn "*addsi_carry"
+ [(set (match_operand:HI 0 "register_operand" "=x")
+ (plus:HI (plus:HI (match_operand:HI 1 "register_operand" "0")
+ (const_int 0))
+ (reg:HI CC_REGNUM)))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = gen_label_rtx ();
+ output_asm_insn (\"bcc\\t%l0\", ops);
+ output_asm_insn (\"in%0\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+;;
+;; - 16-bit Add.
+;;
+(define_expand "addhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (plus:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_M6811 && SP_REG_P (operands[0]))
+ {
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
+ gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_PLUS (HImode,
+ operand1, operand2)),
+ gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_SCRATCH (HImode)))));
+ DONE;
+ }
+}")
+
+(define_insn "*addhi3_68hc12"
+ [(set (match_operand:HI 0 "register_operand" "=d*A,d,xy*A*w,xy*A*w,xy*A")
+ (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,xy*Aw,0")
+ (match_operand:HI 2 "general_operand" "i,m*A*wu,id,id,!mu*A")))]
+ "TARGET_M6812"
+ "*
+{
+ int val;
+ const char* insn_code;
+
+ if (which_alternative >= 4)
+ {
+ if (A_REG_P (operands[2]))
+ {
+ CC_STATUS_INIT;
+ output_asm_insn (\"xgd%2\", operands);
+ output_asm_insn (\"lea%0 d,%0\", operands);
+ return \"xgd%2\";
+ }
+ return \"#\";
+ }
+
+ if (D_REG_P (operands[0]))
+ {
+ if (X_REG_P (operands[2]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn (\"xgdx\", operands);
+ output_asm_insn (\"leax\\td,%2\", operands);
+ return \"xgdx\";
+ }
+ else if (Y_REG_P (operands[2]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn (\"xgdy\", operands);
+ output_asm_insn (\"leay\\td,%2\", operands);
+ return \"xgdy\";
+ }
+ else if (SP_REG_P (operands[2]))
+ {
+ output_asm_insn (\"sts\\t%t0\", operands);
+ return \"addd\\t%t0\";
+ }
+ return \"addd\\t%2\";
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ val = INTVAL (operands[2]);
+ else
+ val = 1000;
+
+ if ((val != -1 && val != 1) || !rtx_equal_p (operands[0], operands[1]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ switch (REGNO (operands[0]))
+ {
+ case HARD_X_REGNUM:
+ return \"leax\\t%i2,%1\";
+
+ case HARD_Y_REGNUM:
+ return \"leay\\t%i2,%1\";
+
+ case HARD_SP_REGNUM:
+ return \"leas\\t%i2,%1\";
+
+ default:
+ fatal_insn (\"Invalid operands in the instruction\", insn);
+ }
+ }
+ if (val > 0)
+ {
+ insn_code = X_REG_P (operands[0]) ? \"inx\"
+ : Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
+ }
+ else
+ {
+ val = -val;
+ insn_code = X_REG_P (operands[0]) ? \"dex\"
+ : Y_REG_P (operands[0]) ? \"dey\" : \"des\";
+ }
+
+ /* For X and Y increment, the flags are not complete. Only the Z flag
+ is updated. For SP increment, flags are not changed. */
+ if (SP_REG_P (operands[0]))
+ {
+ cc_status = cc_prev_status;
+ if (INTVAL (operands[2]) < 0)
+ {
+ while (val > 2)
+ {
+ output_asm_insn (\"pshx\", operands);
+ val -= 2;
+ }
+ if (val == 0)
+ return \"\";
+ }
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ }
+
+ while (val)
+ {
+ output_asm_insn (insn_code, operands);
+ val--;
+ }
+ return \"\";
+}")
+
+;;
+;; Specific pattern to add to the stack pointer.
+;; We also take care of the clobbering of the IY register.
+;;
+(define_insn "addhi_sp"
+ [(set (match_operand:HI 0 "stack_register_operand" "=w,w,w,w")
+ (plus:HI (match_operand:HI 1 "stack_register_operand" "%0,0,0,0")
+ (match_operand:HI 2 "general_operand" "P,im,u,im")))
+ (clobber (match_scratch:HI 3 "=X,&y,&y,!&x"))]
+ "!TARGET_M6812"
+ "*
+{
+ HOST_WIDE_INT val;
+
+ if (optimize && Y_REG_P (operands[3])
+ && dead_register_here (insn, gen_rtx_REG (HImode, HARD_X_REGNUM)))
+ operands[3] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (val = INTVAL (operands[2])) != 0
+ && (CONST_OK_FOR_LETTER_P (val, 'P')
+ || (val > 0 && val <= 8)))
+ {
+ while (val > 1 || val < -1)
+ {
+ if (val > 0)
+ {
+ if (!H_REG_P (operands[3]))
+ break;
+
+ output_asm_insn (\"pul%3\", operands);
+ val -= 2;
+ }
+ else
+ {
+ output_asm_insn (\"pshx\", operands);
+ val += 2;
+ }
+ }
+ while (val != 0)
+ {
+ if (val > 0)
+ {
+ output_asm_insn (\"ins\", operands);
+ val--;
+ }
+ else
+ {
+ output_asm_insn (\"des\", operands);
+ val++;
+ }
+ }
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+
+ /* Need to transfer to SP to X/Y and then to D register.
+ Register X/Y is lost, this is specified by the (clobber) statement. */
+ output_asm_insn (\"ts%3\", operands);
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ((val = INTVAL (operands[2])) >= 0 && val < 0x100)
+ && dead_register_here (insn, gen_rtx_REG (HImode, HARD_D_REGNUM)))
+ {
+ output_asm_insn (\"ldab\\t%2\", operands);
+ output_asm_insn (\"ab%3\", operands);
+ CC_STATUS_INIT;
+ }
+ else
+ {
+ output_asm_insn (\"xgd%3\", operands);
+ output_asm_insn (\"addd\\t%2\", operands);
+ output_asm_insn (\"xgd%3\", operands);
+ }
+
+ /* The status flags correspond to the addd. xgdy and tys do not
+ modify the flags. */
+ return \"t%3s\";
+}")
+
+(define_insn "*addhi3"
+ [(set (match_operand:HI 0 "hard_reg_operand" "=A,dA,d,!A,d*A,d,!d*A")
+ (plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0,0,0,0")
+ (match_operand:HI 2 "general_operand" "N,I,i,I,mi*A*d,*u,!u*d*w")))]
+ "TARGET_M6811"
+ "*
+{
+ const char* insn_code;
+ int val;
+
+ if (D_REG_P (operands[0]) && SP_REG_P (operands[2]))
+ {
+ output_asm_insn (\"sts\\t%t0\", operands);
+ output_asm_insn (\"addd\\t%t0\", operands);
+ return \"addd\\t#1\";
+ }
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ /* Adding to an address register or with another/same register
+ is not possible. This must be replaced. */
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ return \"addd\\t%2\";
+ }
+ val = INTVAL (operands[2]);
+ if (!SP_REG_P (operands[0]))
+ {
+ if (D_REG_P (operands[0]))
+ {
+ if ((val & 0x0ff) == 0 && !next_insn_test_reg (insn, operands[0]))
+ {
+ CC_STATUS_INIT;
+ return \"adda\\t%h2\";
+ }
+ else
+ {
+ return \"addd\\t%2\";
+ }
+ }
+ else if (GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) < -4
+ || INTVAL (operands[2]) > 4)
+ return \"#\";
+ }
+ if (val > 0)
+ {
+ insn_code = X_REG_P (operands[0]) ? \"inx\"
+ : Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
+ }
+ else
+ {
+ val = -val;
+ insn_code = X_REG_P (operands[0]) ? \"dex\"
+ : Y_REG_P (operands[0]) ? \"dey\" : \"des\";
+ }
+
+ /* For X and Y increment, the flags are not complete. Only the Z flag
+ is updated. For SP increment, flags are not changed. */
+ if (SP_REG_P (operands[0]))
+ {
+ cc_status = cc_prev_status;
+ if (INTVAL (operands[2]) < 0)
+ {
+ while (val >= 2)
+ {
+ output_asm_insn (\"pshx\", operands);
+ val -= 2;
+ }
+ }
+ else if (optimize && dead_register_here (insn, ix_reg))
+ {
+ while (val >= 2)
+ {
+ output_asm_insn (\"pulx\", operands);
+ val -= 2;
+ }
+ }
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ }
+
+ while (val)
+ {
+ output_asm_insn (insn_code, operands);
+ val--;
+ }
+ return \"\";
+}")
+
+(define_insn "*addhi3_zext"
+ [(set (match_operand:HI 0 "hard_reg_operand" "=A,d")
+ (plus:HI (zero_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "d,um*A"))
+ (match_operand:HI 2 "general_operand" "0,0")))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (A_REG_P (operands[0]))
+ return \"ab%0\";
+ else if (A_REG_P (operands[1]))
+ return \"st%1\\t%t0\\n\\taddb\\t%T0\\n\\tadca\\t#0\";
+ else
+ return \"addb\\t%b1\\n\\tadca\\t#0\";
+}")
+
+;;
+;; Translate d = d + d into d = << 1
+;; We have to do this because adding a register to itself is not possible.
+;; ??? It's not clear whether this is really necessary.
+;;
+(define_split
+ [(set (match_operand:QI 0 "hard_reg_operand" "")
+ (plus:QI (match_dup 0)
+ (match_dup 0)))]
+ "0 && reload_completed"
+ [(set (match_dup 0) (ashift:QI (match_dup 0) (const_int 1)))]
+ "")
+
+(define_insn "addqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=!d*rm,dq,!*A")
+ (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
+ (match_operand:QI 2 "general_operand" "N,ium*A*d,ium*A*d")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ if (INTVAL (operands[2]) == 1)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ return \"inca\";
+ }
+ else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ {
+ return \"incb\";
+
+ }
+ else if (A_REG_P (operands[0]))
+ {
+ /* This applies on the 16-bit register. This should be ok since
+ this is not a strict_low_part increment. */
+ return \"in%0\";
+ }
+ else
+ {
+ return \"inc\\t%b0\";
+ }
+ }
+ else if (INTVAL (operands[2]) == -1)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ return \"deca\";
+ }
+ else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ {
+ return \"decb\";
+ }
+ else if (A_REG_P (operands[0]))
+ {
+ /* This applies on the 16-bit register. This should be ok since
+ this is not a strict_low_part decrement. */
+ return \"de%0\";
+ }
+ else
+ {
+ return \"dec\\t%b0\";
+ }
+ }
+ }
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+ else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"addb\\t%b2\";
+ else
+ return \"adda\\t%b2\";
+}")
+
+;;
+;; add with carry is used for 32-bit add.
+;;
+(define_insn "*adcq"
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (plus:QI (plus:QI (reg:QI CC_REGNUM)
+ (match_operand:QI 1 "register_operand" "%0"))
+ (match_operand:QI 2 "general_operand" "ium")))]
+ ""
+ "adc%0\\t%b2")
+
+;;--------------------------------------------------------------------
+;;- Subtract instructions.
+;;--------------------------------------------------------------------
+
+(define_expand "subdi3"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (minus:DI (match_operand:DI 1 "nonimmediate_operand" "")
+ (match_operand:DI 2 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"___subdi3\", MINUS, DImode, DImode, 3, operands);
+ DONE;")
+
+;;
+;; 32-bit Subtract (see addsi3)
+;; Subtract with a constant are handled by addsi3.
+;;
+;;
+;; - 32-bit Add.
+;;
+(define_expand "subsi3"
+ [(parallel [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 ""))])]
+ ""
+ "")
+
+(define_insn "*subsi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D,D,!u")
+ (minus:SI (match_operand:SI 1 "general_operand" "0,oi,0,!u,0")
+ (match_operand:SI 2 "general_operand" "oi,D,!u,D,!oui")))
+ (clobber (match_scratch:HI 3 "=X,X,X,X,d"))]
+ ""
+ "#")
+
+(define_insn "*subsi3_zero_extendhi"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (minus:SI (match_operand:SI 1 "register_operand" "0")
+ (zero_extend:SI (match_operand:HI 2 "general_operand" "dmui*A"))))
+ (clobber (match_scratch:HI 3 "=X"))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (A_REG_P (operands[2]))
+ {
+ if (TARGET_M6812)
+ ops[0] = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ else
+ ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+
+ ops[1] = operands[2];
+ m68hc11_gen_movhi (insn, ops);
+ if (TARGET_M6812)
+ operands[2] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ else
+ operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ }
+ ops[0] = gen_label_rtx ();
+ output_asm_insn (\"subd\\t%2\", operands);
+ output_asm_insn (\"bcc\\t%l0\", ops);
+ output_asm_insn (\"dex\", ops);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+(define_insn "*subsi3_zero_extendqi"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (minus:SI (match_operand:SI 1 "register_operand" "0")
+ (zero_extend:SI (match_operand:QI 2 "general_operand" "dmui*A"))))
+ (clobber (match_scratch:HI 3 "=X"))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (A_REG_P (operands[2]))
+ {
+ ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movhi (insn, ops);
+ operands[2] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ }
+ ops[0] = gen_label_rtx ();
+ output_asm_insn (\"subb\\t%b2\", operands);
+ output_asm_insn (\"sbca\\t#0\", operands);
+ output_asm_insn (\"bcc\\t%l0\", ops);
+ output_asm_insn (\"dex\", ops);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+;;
+;; reg:HI 1 -> d reg:QI 6 -> B
+;; reg:QI 7 -> ccr reg:QI 5 -> A
+;;
+(define_split /* "*subsi3" */
+ [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "reload_completed && z_replacement_completed == 2
+ && X_REG_P (operands[1])"
+ [(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
+ (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
+ (set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
+ (set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
+ (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])]
+ "operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
+ operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
+
+(define_split /* "*subsi3" */
+ [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "register_operand" "")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "reload_completed && z_replacement_completed == 2
+ && X_REG_P (operands[2])"
+ [(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
+ (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
+ (set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
+ (set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
+ (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
+ (set (reg:SI 0) (neg:SI (reg:SI 0)))]
+ "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
+ operands[4] = m68hc11_gen_highpart (HImode, operands[1]);
+ operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
+
+(define_split /* "*subsi3" */
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (minus:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 "=d"))]
+ "reload_completed && z_replacement_completed == 2
+ && !X_REG_P (operands[0])"
+ [(set (match_dup 3) (match_dup 4))
+ (set (match_dup 3) (minus:HI (match_dup 3) (match_dup 5)))
+ (set (match_dup 4) (match_dup 3))
+ (set (match_dup 3) (match_dup 6))
+ (set (reg:QI 6) (minus:QI (minus:QI (reg:QI 7) (reg:QI 6)) (match_dup 7)))
+ (set (reg:QI 5) (minus:QI (minus:QI (reg:QI 7) (reg:QI 5)) (match_dup 8)))
+ (set (match_dup 6) (match_dup 3))]
+ "operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
+ operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
+ operands[7] = m68hc11_gen_highpart (HImode, operands[2]);
+ operands[8] = m68hc11_gen_highpart (QImode, operands[7]);
+ operands[7] = m68hc11_gen_lowpart (QImode, operands[7]);")
+
+;;
+;; - 16-bit Subtract.
+;;
+(define_expand "subhi3"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (minus:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "general_operand" "g")))]
+ ""
+ "")
+
+;;
+;; Subtract from stack. This is better if we provide a pattern.
+;;
+(define_insn "*subhi3_sp"
+ [(set (match_operand:HI 0 "stack_register_operand" "=w,w")
+ (minus:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "im*d,!u*A")))
+ (clobber (match_scratch:HI 3 "=A*d,A*d"))]
+ ""
+ "*
+{
+ if (X_REG_P (operands[2]))
+ {
+ operands[2] = m68hc11_soft_tmp_reg;
+ output_asm_insn (\"stx\\t%2\", operands);
+ }
+ else if (Y_REG_P (operands[2]))
+ {
+ operands[2] = m68hc11_soft_tmp_reg;
+ output_asm_insn (\"sty\\t%2\", operands);
+ }
+ else if (D_REG_P (operands[2]))
+ {
+ operands[2] = m68hc11_soft_tmp_reg;
+ output_asm_insn (\"std\\t%2\", operands);
+ }
+
+ if (D_REG_P (operands[3]))
+ {
+ int save_x;
+
+ save_x = !dead_register_here (insn, ix_reg);
+ if (save_x)
+ output_asm_insn (\"xgdx\", operands);
+ output_asm_insn (\"tsx\", operands);
+ output_asm_insn (\"xgdx\", operands);
+ output_asm_insn (\"subd\\t%2\", operands);
+ output_asm_insn (\"xgdx\", operands);
+
+ /* The status flags correspond to the addd. xgdx/y and tx/ys do not
+ modify the flags. */
+ output_asm_insn (\"txs\", operands);
+ if (save_x)
+ return \"xgdx\";
+ else
+ return \"\";
+ }
+
+ /* Need to transfer to SP to X,Y and then to D register.
+ Register X,Y is lost, this is specified by the (clobber) statement. */
+ output_asm_insn (\"ts%3\", operands);
+ output_asm_insn (\"xgd%3\", operands);
+ output_asm_insn (\"subd\\t%2\", operands);
+ output_asm_insn (\"xgd%3\", operands);
+
+ /* The status flags correspond to the addd. xgdx/y and tx/ys do not
+ modify the flags. */
+ return \"t%3s\";
+}")
+
+
+(define_insn "*subhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,*A,d,*A")
+ (minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
+ (match_operand:HI 2 "general_operand" "im*A*d,im*d*A,u,!u")))]
+ ""
+ "*
+{
+ /* Adding to an address register or with another/same register
+ is not possible. This must be replaced. */
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ return \"subd\\t%2\";
+}")
+
+(define_insn "*subhi3_zext"
+ [(set (match_operand:HI 0 "hard_reg_operand" "=d,d")
+ (minus:HI (match_operand:HI 1 "general_operand" "0,0")
+ (zero_extend:HI (match_operand:QI 2 "general_operand" "mi*A,!u"))))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (A_REG_P (operands[2]))
+ {
+ rtx ops[2];
+
+ ops[0] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+ return \"subb\\t%T0\\n\\tsbca\\t#0\";
+ }
+ return \"subb\\t%b2\\n\\tsbca\\t#0\";
+}")
+
+(define_insn "subqi3"
+ [(set (match_operand:QI 0 "hard_reg_operand" "=dq,!*x*y")
+ (minus:QI (match_operand:QI 1 "general_operand" "0,0")
+ (match_operand:QI 2 "general_operand" "uim*A*d,uim*A*d")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+ else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"subb\\t%b2\";
+ else
+ return \"suba\\t%b2\";
+}")
+
+;;
+;; subtract with carry is used for 32-bit subtract.
+;;
+(define_insn "*subcq"
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (minus:QI (minus:QI (reg:QI CC_REGNUM)
+ (match_operand:QI 1 "register_operand" "0"))
+ (match_operand:QI 2 "general_operand" "ium")))]
+ ""
+ "sbc%0\\t%b2")
+
+;;--------------------------------------------------------------------
+;;- Multiply instructions.
+;;--------------------------------------------------------------------
+;;
+;; 32 and 64-bit multiply are handled by the library
+;;
+
+(define_expand "mulsi3"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (mult:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"__mulsi3\", MULT, SImode, SImode, 3, operands);
+ DONE;")
+
+(define_expand "mulhi3"
+ [(parallel [(set (match_operand:HI 0 "register_operand" "")
+ (mult:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "register_operand" "")))
+ (clobber (match_scratch:HI 3 ""))])]
+ ""
+ "")
+
+(define_insn "mulhi3_m68hc11"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (mult:HI (match_operand:HI 1 "register_operand" "%0")
+ (match_operand:HI 2 "register_operand" "x")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "TARGET_M6811"
+ "*
+{
+ CC_STATUS_INIT;
+ /* D * X -> D (X and Y are preserved by this function call). */
+ return \"jsr\\t___mulhi3\";
+}")
+
+(define_insn "mulhi3_m68hc12"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (mult:HI (match_operand:HI 1 "register_operand" "%0,0")
+ (match_operand:HI 2 "register_operand" "y,x")))
+ (clobber (match_scratch:HI 3 "=2,2"))]
+ "TARGET_M6812"
+ "*
+{
+ CC_STATUS_INIT;
+ if (X_REG_P (operands[2]))
+ return \"exg\\tx,y\\n\\temul\\n\\texg\\tx,y\";
+ else
+ return \"emul\";
+}")
+
+(define_insn "umulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,D")
+ (mult:SI (zero_extend:SI
+ (match_operand:HI 1 "register_operand" "%d,d"))
+ (zero_extend:SI
+ (match_operand:HI 2 "register_operand" "y,x"))))
+ (clobber (match_scratch:HI 3 "=2,X"))]
+ "TARGET_M6812"
+ "*
+{
+ if (X_REG_P (operands [2]))
+ output_asm_insn (\"exg\\tx,y\", operands);
+
+ /* Can't use the carry after that; other flags are ok when testing
+ the 32-bit result. */
+ cc_status.flags |= CC_NO_OVERFLOW;
+ return \"emul\\n\\texg\\tx,y\";
+}")
+
+(define_insn "mulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,D")
+ (mult:SI (sign_extend:SI
+ (match_operand:HI 1 "register_operand" "%d,d"))
+ (sign_extend:SI
+ (match_operand:HI 2 "register_operand" "y,x"))))
+ (clobber (match_scratch:HI 3 "=2,X"))]
+ "TARGET_M6812"
+ "*
+{
+ if (X_REG_P (operands [2]))
+ output_asm_insn (\"exg\\tx,y\", operands);
+
+ /* Can't use the carry after that; other flags are ok when testing
+ the 32-bit result. */
+ cc_status.flags |= CC_NO_OVERFLOW;
+ return \"emuls\\n\\texg\\tx,y\";
+}")
+
+(define_insn "umulqihi3"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (mult:HI (zero_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "dm*u"))
+ (zero_extend:HI
+ (match_operand:QI 2 "nonimmediate_operand" "dm*u*A"))))]
+ ""
+ "*
+{
+ if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
+ {
+ output_asm_insn (\"tba\", operands);
+ }
+ else
+ {
+ rtx ops[2];
+
+ if (D_REG_P (operands[2]))
+ {
+ rtx temp = operands[2];
+ operands[2] = operands[1];
+ operands[1] = temp;
+ }
+
+ ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+
+ if (!D_REG_P (operands[1]))
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ }
+ }
+
+ CC_STATUS_INIT;
+ return \"mul\";
+}")
+
+(define_insn "mulqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,*x,*y")
+ (mult:QI (match_operand:QI 1 "general_operand" "%di*um,0,0")
+ (match_operand:QI 2 "general_operand" "di*um,*xium,*yium")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
+ {
+ output_asm_insn (\"tba\", operands);
+ }
+ else
+ {
+ if (D_REG_P (operands[2]))
+ {
+ rtx temp = operands[2];
+ operands[2] = operands[1];
+ operands[1] = temp;
+ }
+
+ output_asm_insn (\"ldaa\\t%b2\", operands);
+
+ if (!D_REG_P (operands[1]))
+ {
+ output_asm_insn (\"ldab\\t%b1\", operands);
+ }
+ }
+
+ CC_STATUS_INIT;
+ return \"mul\";
+}")
+
+(define_split
+ [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (mult:QI (match_operand:QI 1 "general_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ "z_replacement_completed == 2"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (mult:QI (match_dup 5) (match_dup 6)))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])]
+ "
+ operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
+ if (A_REG_P (operands[1]))
+ operands[5] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ else
+ operands[5] = operands[1];
+ if (A_REG_P (operands[2]))
+ operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ else
+ operands[6] = operands[2];
+ ")
+
+(define_insn "mulqihi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,d,d")
+ (mult:HI (sign_extend:HI
+ (match_operand:QI 1 "register_operand" "%0,0,0"))
+ (sign_extend:HI
+ (match_operand:QI 2 "general_operand" "mi*u,*A,0"))))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+
+ /* Special case when multiplying the register with itself. */
+ if (D_REG_P (operands[2]))
+ {
+ output_asm_insn (\"tba\", operands);
+ return \"mul\";
+ }
+
+ if (!H_REG_P (operands[2]))
+ {
+ output_asm_insn (\"ldaa\\t%b2\", operands);
+ }
+ else
+ {
+ rtx ops[2];
+
+ ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+ }
+ return \"jsr\\t___mulqi3\";
+}")
+
+;;--------------------------------------------------------------------
+;;- Divide instructions.
+;;--------------------------------------------------------------------
+
+(define_insn "divmodhi4"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (div:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "A,ium")))
+ (set (match_operand:HI 3 "register_operand" "=&x,&x")
+ (mod:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "*
+{
+ if (!X_REG_P (operands[2]))
+ {
+ if (Y_REG_P (operands[2]))
+ {
+ output_asm_insn (\"sty\\t%t1\", operands);
+ output_asm_insn (\"ldx\\t%t1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldx\\t%2\", operands);
+ }
+ }
+ if (TARGET_M6812)
+ {
+ /* Flags are ok after that. */
+ return \"idivs\\n\\txgdx\";
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ return \"bsr\\t__divmodhi4\";
+ }
+}")
+
+(define_insn "udivmodhi4"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (udiv:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "A,ium")))
+ (set (match_operand:HI 3 "register_operand" "=x,x")
+ (umod:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "*
+{
+ if (!X_REG_P (operands[2]))
+ {
+ if (Y_REG_P (operands[2]))
+ {
+ output_asm_insn (\"sty\\t%t1\", operands);
+ output_asm_insn (\"ldx\\t%t1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldx\\t%2\", operands);
+ }
+ }
+
+ /* Z V and C flags are set but N is unchanged.
+ Since this is an unsigned divide, we can probably keep the flags
+ and indicate this. */
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return \"idiv\\n\\txgdx\";
+}")
+
+;;--------------------------------------------------------------------
+;;- and instructions.
+;;--------------------------------------------------------------------
+
+(define_insn_and_split "anddi3"
+ [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
+ (and:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
+ (match_operand:DI 2 "general_operand" "imu,imu")))
+ (clobber (match_scratch:HI 3 "=d,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (SImode, AND, operands);
+ DONE;")
+
+(define_insn_and_split "andsi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,!u")
+ (and:SI (match_operand:SI 1 "register_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "Dimu,imu")))
+ (clobber (match_scratch:HI 3 "=X,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (HImode, AND, operands);
+ DONE;")
+
+(define_expand "andhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (and:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*andhi3_mem"
+ [(set (match_operand:HI 0 "memory_operand" "=R,Q")
+ (and:HI (match_dup 0)
+ (match_operand:HI 1 "immediate_operand" "i,i")))
+ (clobber (match_scratch:HI 2 "=X,xy"))]
+ "TARGET_RELAX && !TARGET_M6812"
+ "*
+{
+ int val = INTVAL (operands[1]) & 0x0FFFF;
+
+ if (val == 0x0ffff)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+
+ CC_STATUS_INIT;
+
+ /* The bclr instruction uses an inverted mask. */
+ operands[1] = GEN_INT ((~val) & 0x0FFFF);
+
+ /* When destination is a global variable, generate a .relax instruction
+ and load the address in the clobber register. That load can be
+ eliminated by the linker if the address is in page0. */
+ if (which_alternative == 1)
+ {
+ rtx ops[3];
+
+ ops[0] = operands[2];
+ ops[1] = XEXP (operands[0], 0);
+ ops[2] = gen_label_rtx ();
+ output_asm_insn (\".relax\\t%l2\", ops);
+ m68hc11_gen_movhi (insn, ops);
+ if ((val & 0x0FF) != 0x0FF)
+ output_asm_insn (\"bclr\\t1,%2, %b1\", operands);
+
+ if ((val & 0x0FF00) != 0x0FF00)
+ output_asm_insn (\"bclr\\t0,%2, %h1\", operands);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[2]));
+ return \"\";
+ }
+
+ if ((val & 0x0FF) != 0x0FF)
+ output_asm_insn (\"bclr\\t%b0, %b1\", operands);
+
+ if ((val & 0x0FF00) != 0x0FF00)
+ output_asm_insn (\"bclr\\t%h0, %h1\", operands);
+
+ return \"\";
+}")
+
+(define_insn "*andhi3_const"
+ [(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
+ (and:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
+ (match_operand:HI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int val = INTVAL (operands[2]) & 0x0FFFF;
+ int lowpart_zero = 0;
+ int highpart_zero = 0;
+ int lowpart_unknown = 0;
+ int highpart_unknown = 0;
+
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (val == 0x0ffff)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+
+ /* First, try to clear the low and high part.
+ If that's possible, the second 'and' will give
+ the good status flags and we can avoid a tsthi. */
+ if ((val & 0x0FF) == 0)
+ {
+ if (D_REG_P (operands[0]))
+ output_asm_insn (\"clrb\", operands);
+ else
+ output_asm_insn (\"clr\\t%b0\", operands);
+ lowpart_zero = 1;
+ }
+ if ((val & 0x0FF00) == 0)
+ {
+ if (D_REG_P (operands[0]))
+ output_asm_insn (\"clra\", operands);
+ else
+ output_asm_insn (\"clr\\t%h0\", operands);
+ highpart_zero = 1;
+ }
+
+ if ((val & 0x0FF) == 0x0FF)
+ {
+ lowpart_unknown = 1;
+ }
+ else if ((val & 0x0FF) != 0 && !H_REG_P (operands[0]))
+ {
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = GEN_INT ((~val) & 0x0FF);
+ output_asm_insn (\"bclr\\t%b0, %1\", ops);
+ }
+ else if ((val & 0x0FF) != 0)
+ {
+ output_asm_insn (\"andb\\t%b2\", operands);
+ }
+
+ if ((val & 0x0FF00) == 0x0FF00)
+ {
+ highpart_unknown = 1;
+ }
+ else if (((val & 0x0FF00) != 0) && !H_REG_P (operands[0]))
+ {
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = GEN_INT (((~val) & 0x0FF00) >> 8);
+ output_asm_insn (\"bclr\\t%h0, %1\", ops);
+ }
+ else if ((val & 0x0FF00) != 0)
+ {
+ output_asm_insn (\"anda\\t%h2\", operands);
+ }
+
+ if (highpart_unknown || lowpart_unknown)
+ CC_STATUS_INIT;
+ else if (highpart_zero == 0 && lowpart_zero == 0)
+ CC_STATUS_INIT;
+
+ return \"\";
+}")
+
+(define_insn "*andhi3_gen"
+ [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
+ (and:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
+ (match_operand:HI 2 "splitable_operand" "mi,!u*A,!um*Ai")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ return \"anda\\t%h2\\n\\tandb\\t%b2\";
+}")
+
+(define_expand "andqi3"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (and:QI (match_operand:QI 1 "register_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*andqi3_mem"
+ [(set (match_operand:QI 0 "memory_operand" "=R,Q")
+ (and:QI (match_dup 0)
+ (match_operand:QI 1 "const_int_operand" "i,i")))
+ (clobber (match_scratch:HI 2 "=X,xy"))]
+ "TARGET_RELAX && !TARGET_M6812"
+ "*
+{
+ int val = INTVAL (operands[1]) & 0x0FF;
+
+ if (val == 0x0ff)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+
+ /* The bclr instruction uses an inverted mask. */
+ operands[1] = GEN_INT ((~val) & 0x0FF);
+
+ /* When destination is a global variable, generate a .relax instruction
+ and load the address in the clobber register. That load can be
+ eliminated by the linker if the address is in page0. */
+ if (which_alternative == 1)
+ {
+ rtx ops[3];
+
+ ops[0] = operands[2];
+ ops[1] = XEXP (operands[0], 0);
+ ops[2] = gen_label_rtx ();
+ output_asm_insn (\".relax\\t%l2\", ops);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"bclr\\t0,%2, %1\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[2]));
+ return \"\";
+ }
+ return \"bclr\\t%b0, %1\";
+}")
+
+(define_insn "*andqi3_const"
+ [(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
+ (and:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
+ (match_operand:QI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int val = INTVAL (operands[2]) & 0x0FF;
+
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (val == 0x0ff)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ if (!H_REG_P (operands[0]))
+ {
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = GEN_INT ((~val) & 0x0FF);
+ output_asm_insn (\"bclr\\t%b0, %b1\", ops);
+ return \"\";
+ }
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"andb\\t%b2\";
+ else if (DA_REG_P (operands[0]))
+ return \"anda\\t%b2\";
+ else
+ fatal_insn (\"Invalid operand in the instruction\", insn);
+}")
+
+(define_insn "*andqi3_gen"
+ [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
+ (and:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
+ (match_operand:QI 2 "general_operand" "mi,!*u,?*A,!*um,?*A*d,!*um*A")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"andb\\t%b2\";
+ else if (DA_REG_P (operands[0]))
+ return \"anda\\t%b2\";
+ else
+ fatal_insn (\"Invalid operand in the instruction\", insn);
+}")
+
+;;--------------------------------------------------------------------
+;;- Bit set or instructions.
+;;--------------------------------------------------------------------
+
+(define_insn_and_split "iordi3"
+ [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
+ (ior:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
+ (match_operand:DI 2 "general_operand" "imu,imu")))
+ (clobber (match_scratch:HI 3 "=d,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (SImode, IOR, operands);
+ DONE;")
+
+(define_insn_and_split "iorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,!u")
+ (ior:SI (match_operand:SI 1 "register_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "Dimu,imu")))
+ (clobber (match_scratch:HI 3 "=X,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (HImode, IOR, operands);
+ DONE;")
+
+(define_expand "iorhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (ior:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "splitable_operand" "")))]
+ ""
+ "")
+
+(define_insn "*iorhi3_mem"
+ [(set (match_operand:HI 0 "memory_operand" "=R,Q")
+ (ior:HI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (clobber (match_scratch:HI 2 "=X,xy"))]
+ "TARGET_RELAX && !TARGET_M6812"
+ "*
+{
+ int val = INTVAL (operands[1]) & 0x0FFFF;
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ CC_STATUS_INIT;
+ if (which_alternative == 1)
+ {
+ rtx ops[3];
+
+ ops[0] = operands[2];
+ ops[1] = XEXP (operands[0], 0);
+ ops[2] = gen_label_rtx ();
+ output_asm_insn (\".relax\\t%l2\", ops);
+ m68hc11_gen_movhi (insn, ops);
+ if ((val & 0x0FF) != 0)
+ output_asm_insn (\"bset\\t1,%2, %b1\", operands);
+
+ if ((val & 0x0FF00) != 0)
+ output_asm_insn (\"bset\\t0,%2, %h1\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[2]));
+ return \"\";
+ }
+
+ if ((val & 0x0FF) != 0)
+ output_asm_insn (\"bset\\t%b0, %b1\", operands);
+
+ if ((val & 0x0FF00) != 0)
+ output_asm_insn (\"bset\\t%h0, %h1\", operands);
+
+ return \"\";
+}")
+
+(define_insn "*iorhi3_const"
+ [(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
+ (ior:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
+ (match_operand:HI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int val = INTVAL (operands[2]) & 0x0FFFF;
+
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+
+ if ((val & 0x0FF) != 0)
+ {
+ if (!H_REG_P (operands[0]))
+ output_asm_insn (\"bset\\t%b0, %b2\", operands);
+ else
+ output_asm_insn (\"orab\\t%b2\", operands);
+ }
+
+ if ((val & 0x0FF00) != 0)
+ {
+ if (!H_REG_P (operands[0]))
+ output_asm_insn (\"bset\\t%h0, %h2\", operands);
+ else
+ output_asm_insn (\"oraa\\t%h2\", operands);
+ }
+
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+(define_insn "*iorhi3_gen"
+ [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
+ (ior:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
+ (match_operand:HI 2 "splitable_operand" "mi,!u*A,!um*Ai")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ return \"oraa\\t%h2\\n\\torab\\t%b2\";
+}")
+
+(define_expand "iorqi3"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (ior:QI (match_operand:QI 1 "register_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*iorqi3_mem"
+ [(set (match_operand:QI 0 "memory_operand" "=R,Q")
+ (ior:QI (match_dup 0)
+ (match_operand:QI 1 "const_int_operand" "")))
+ (clobber (match_scratch:HI 2 "=X,xy"))]
+ "TARGET_RELAX && !TARGET_M6812"
+ "*
+{
+ int val = INTVAL (operands[1]) & 0x0FF;
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ if (which_alternative == 1)
+ {
+ rtx ops[3];
+
+ ops[0] = operands[2];
+ ops[1] = XEXP (operands[0], 0);
+ ops[2] = gen_label_rtx ();
+ output_asm_insn (\".relax\\t%l2\", ops);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"bset\\t0,%2, %1\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[2]));
+ return \"\";
+ }
+ return \"bset\\t%b0, %1\";
+}")
+
+(define_insn "*iorqi3_const"
+ [(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
+ (ior:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
+ (match_operand:QI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int val = INTVAL (operands[2]) & 0x0FF;
+
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ if (!H_REG_P (operands[0]))
+ {
+ return \"bset\\t%b0, %2\";
+ }
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"orab\\t%b2\";
+ else if (DA_REG_P (operands[0]))
+ return \"oraa\\t%b2\";
+ else
+ fatal_insn (\"Invalid operand in the instruction\", insn);
+}")
+
+(define_insn "*iorqi3_gen"
+ [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
+ (ior:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
+ (match_operand:QI 2 "general_operand" "mi,!*u,!*A,!*um,?*A*d,!*um*A")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"orab\\t%b2\";
+ else if (DA_REG_P (operands[0]))
+ return \"oraa\\t%b2\";
+ else
+ fatal_insn (\"Invalid operand in the instruction\", insn);
+}")
+
+
+;;--------------------------------------------------------------------
+;;- xor instructions.
+;;--------------------------------------------------------------------
+
+(define_insn_and_split "xordi3"
+ [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
+ (xor:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
+ (match_operand:DI 2 "general_operand" "imu,imu")))
+ (clobber (match_scratch:HI 3 "=d,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (SImode, XOR, operands);
+ DONE;")
+
+(define_insn_and_split "xorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=D,!u")
+ (xor:SI (match_operand:SI 1 "register_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "Dimu,imu")))
+ (clobber (match_scratch:HI 3 "=X,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_logical (HImode, XOR, operands);
+ DONE;")
+
+(define_insn "xorhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
+ (xor:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
+ (match_operand:HI 2 "splitable_operand" "im,!u*A,!ium*A")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ int val = INTVAL (operands[2]) & 0x0FFFF;
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ if ((val & 0x0FF) != 0)
+ {
+ output_asm_insn (\"eorb\\t%b2\", operands);
+ }
+ else if ((val & 0x0FF) == 0x0FF)
+ {
+ output_asm_insn (\"comb\", operands);
+ }
+
+ if ((val & 0x0FF00) != 0)
+ {
+ output_asm_insn (\"eora\\t%h2\", operands);
+ }
+ else if ((val & 0x0FF00) == 0x0FF00)
+ {
+ output_asm_insn (\"coma\", operands);
+ }
+
+ CC_STATUS_INIT;
+ return \"\";
+ }
+
+ CC_STATUS_INIT;
+ return \"eora\\t%h2\\n\\teorb\\t%b2\";
+}")
+
+(define_insn "xorqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
+ (xor:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
+ (match_operand:QI 2 "general_operand" "im,!*u,!*A,!i*um,?*A*d,!i*um*A")))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
+ return \"#\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ int val = INTVAL (operands[2]) & 0x0FF;
+
+ if (val == 0)
+ {
+ cc_status = cc_prev_status;
+ return \"\";
+ }
+ if (val == 0x0FF)
+ {
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"comb\";
+ else
+ return \"coma\";
+ }
+ }
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ return \"eorb\\t%b2\";
+ else if (DA_REG_P (operands[0]))
+ return \"eora\\t%b2\";
+ else
+ fatal_insn (\"Invalid operand in the instruction\", insn);
+}")
+
+;;--------------------------------------------------------------------
+;;- Bit set or instructions.
+;;--------------------------------------------------------------------
+
+(define_insn_and_split "*logicalsi3_zexthi"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (match_operator:SI 3 "m68hc11_logical_operator"
+ [(zero_extend:SI
+ (match_operand:HI 1 "general_operand" "imudA"))
+ (match_operand:SI 2 "general_operand" "Dimu")]))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (reg:HI D_REGNUM) (match_dup 4))
+ (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
+ (set (reg:HI X_REGNUM) (match_dup 6))]
+ "PUT_MODE (operands[3], HImode);
+ if (X_REG_P (operands[2]))
+ {
+ operands[5] = operands[1];
+ /* Make all the (set (REG:x) (REG:y)) a nop set. */
+ operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ }
+ else
+ {
+ operands[4] = operands[1];
+ operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
+ }
+ /* For an AND, make sure the high 16-bit part is cleared. */
+ if (GET_CODE (operands[3]) == AND)
+ {
+ operands[6] = const0_rtx;
+ }
+ ")
+
+(define_insn_and_split "*logicalsi3_zextqi"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D")
+ (match_operator:SI 3 "m68hc11_logical_operator"
+ [(zero_extend:SI
+ (match_operand:QI 1 "general_operand" "d,*A,imu"))
+ (match_operand:SI 2 "general_operand" "imu,imu,0")]))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (reg:QI A_REGNUM) (match_dup 4))
+ (set (reg:QI D_REGNUM) (match_dup 7))
+ (set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))
+ (set (reg:HI X_REGNUM) (match_dup 6))]
+ "PUT_MODE (operands[3], QImode);
+ if (X_REG_P (operands[2]))
+ {
+ operands[5] = operands[1];
+ /* Make all the (set (REG:x) (REG:y)) a nop set. */
+ operands[4] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ operands[7] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ }
+ else
+ {
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[7] = operands[1];
+ operands[5] = m68hc11_gen_lowpart (QImode, operands[4]);
+ operands[4] = m68hc11_gen_highpart (QImode, operands[4]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
+ }
+ /* For an AND, make sure the high 24-bit part is cleared. */
+ if (GET_CODE (operands[3]) == AND)
+ {
+ operands[4] = const0_rtx;
+ operands[6] = const0_rtx;
+ }
+ ")
+
+(define_insn_and_split "*logicalhi3_zexthi_ashift8"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (match_operator:HI 3 "m68hc11_logical_operator"
+ [(zero_extend:HI
+ (match_operand:QI 1 "general_operand" "imud*A"))
+ (ashift:HI
+ (match_operand:HI 2 "general_operand" "imud*A")
+ (const_int 8))]))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (reg:QI A_REGNUM) (match_dup 4))
+ (set (reg:QI B_REGNUM) (match_dup 5))]
+ "
+ if (GET_CODE (operands[3]) == AND)
+ {
+ emit_insn (gen_movhi (operands[0], const0_rtx));
+ DONE;
+ }
+ else
+ {
+ operands[5] = operands[1];
+ if (D_REG_P (operands[2]))
+ {
+ operands[4] = gen_rtx_REG (QImode, HARD_B_REGNUM);
+ }
+ else
+ {
+ operands[4] = m68hc11_gen_lowpart (QImode, operands[2]);
+ }
+ }
+ ")
+
+(define_insn_and_split "*logicalhi3_zexthi"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (match_operator:HI 3 "m68hc11_logical_operator"
+ [(zero_extend:HI
+ (match_operand:QI 1 "general_operand" "imd*A,?u"))
+ (match_operand:HI 2 "general_operand" "dim,?dimu")]))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (reg:QI B_REGNUM) (match_dup 6))
+ (set (reg:QI A_REGNUM) (match_dup 4))
+ (set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))]
+ "
+ PUT_MODE (operands[3], QImode);
+ if (D_REG_P (operands[2]))
+ {
+ operands[4] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ operands[5] = operands[1];
+ operands[6] = gen_rtx_REG (QImode, HARD_B_REGNUM);
+ }
+ else
+ {
+ operands[4] = m68hc11_gen_highpart (QImode, operands[2]);
+ operands[5] = m68hc11_gen_lowpart (QImode, operands[2]);
+ if (D_REG_P (operands[1]))
+ operands[6] = gen_rtx_REG (QImode, HARD_B_REGNUM);
+ else
+ operands[6] = operands[1];
+ }
+ /* For an AND, make sure the high 8-bit part is cleared. */
+ if (GET_CODE (operands[3]) == AND)
+ {
+ operands[4] = const0_rtx;
+ }
+ ")
+
+
+(define_insn_and_split "*logicalsi3_silshr16"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D,?D")
+ (match_operator:SI 3 "m68hc11_logical_operator"
+ [(lshiftrt:SI
+ (match_operand:SI 1 "general_operand" "uim,uim,0,0")
+ (const_int 16))
+ (match_operand:SI 2 "general_operand" "uim,0,uim,0")]))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (reg:HI D_REGNUM) (match_dup 4))
+ (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
+ (set (reg:HI X_REGNUM) (match_dup 6))]
+ "operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
+ if (X_REG_P (operands[2]))
+ {
+ operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ }
+ else
+ {
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
+ }
+ PUT_MODE (operands[3], HImode);
+
+ /* For an AND, make sure the high 16-bit part is cleared. */
+ if (GET_CODE (operands[3]) == AND)
+ {
+ operands[6] = const0_rtx;
+ }
+")
+
+(define_insn_and_split "*logicalsi3_silshl16"
+ [(set (match_operand:SI 0 "register_operand" "=D,D")
+ (match_operator:SI 3 "m68hc11_logical_operator"
+ [(ashift:SI
+ (match_operand:SI 1 "general_operand" "uim,?D")
+ (const_int 16))
+ (match_operand:SI 2 "general_operand" "0,0")]))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (reg:HI X_REGNUM) (match_op_dup 3 [(reg:HI X_REGNUM) (match_dup 4)]))
+ (set (reg:HI D_REGNUM) (match_dup 5))]
+ "operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
+ PUT_MODE (operands[3], HImode);
+
+ if (GET_CODE (operands[3]) == AND)
+ operands[5] = const0_rtx;
+ else
+ operands[5] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ ")
+
+(define_insn_and_split "*logicalsi3_silshl16_zext"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,D")
+ (match_operator:SI 3 "m68hc11_logical_operator"
+ [(ashift:SI
+ (zero_extend:SI
+ (match_operand:HI 1 "general_operand" "uim,udA,!dA"))
+ (const_int 16))
+ (zero_extend:SI (match_operand:HI 2 "general_operand" "uidA,um,!dA"))]))]
+ ""
+ "#"
+ ;; Must split before z register replacement
+ "reload_completed"
+ [(set (match_dup 4) (match_dup 5))
+ (set (match_dup 6) (match_dup 7))]
+ "
+ /* set (X_REGNUM) (d), set (D_REGNUM) (1) */
+ if (GET_CODE (operands[1]) == HARD_D_REGNUM
+ && GET_CODE (operands[3]) != AND)
+ {
+ /* This particular case is too early to be split before
+ Z register replacement because the cse-reg pass we do
+ does not recognize the 'swap_areg'. It is ok to handle
+ this case after. */
+ if (z_replacement_completed != 2)
+ {
+ FAIL;
+ }
+ emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM), operands[2]);
+ emit_insn (gen_swap_areg (gen_rtx_REG (HImode, HARD_D_REGNUM),
+ gen_rtx_REG (HImode, HARD_X_REGNUM)));
+ }
+ operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ operands[5] = operands[2];
+ operands[7] = operands[1];
+
+ if (GET_CODE (operands[3]) == AND)
+ operands[5] = operands[7] = const0_rtx;
+ ")
+
+;;--------------------------------------------------------------------
+;; 16-bit Arithmetic and logical operations on X and Y:
+;;
+;; PLUS MINUS AND IOR XOR ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
+;;
+;; Operations on X or Y registers are split here. Instructions are
+;; changed into:
+;; - xgdx/xgdy instruction pattern,
+;; - The same operation on register D,
+;; - xgdx/xgdy instruction pattern.
+;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
+;; We also handle the case were the address register is used in both source
+;; operands, such as:
+;;
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
+;; or
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
+;;
+;;
+(define_split
+ [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
+ (match_operator:HI 3 "m68hc11_arith_operator"
+ [(match_operand:HI 1 "hard_addr_reg_operand" "")
+ (match_operand:HI 2 "general_operand" "")]))]
+ "z_replacement_completed == 2
+ /* If we are adding a small constant to X or Y, it's
+ better to use one or several inx/iny instructions. */
+ && !(GET_CODE (operands[3]) == PLUS
+ && ((TARGET_M6812
+ && (immediate_operand (operands[2], HImode)
+ || hard_reg_operand (operands[2], HImode)))
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) >= -4
+ && INTVAL (operands[2]) <= 4)))"
+ [(set (match_dup 9) (match_dup 0))
+ (set (match_dup 4) (match_dup 5))
+ (set (match_dup 8) (match_dup 7))
+ (set (match_dup 0) (match_dup 1))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 6)]))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "
+ operands[9] = operands[0];
+ /* For 68HC12, push the value on the stack and do the operation
+ with a pop. */
+ if (TARGET_M6812
+ && m68hc11_non_shift_operator (operands[3], HImode)
+ && (H_REG_P (operands[2])
+ || (m68hc11_small_indexed_indirect_p (operands[2], HImode)
+ && reg_mentioned_p (operands[0], operands[2]))))
+ {
+ operands[4] = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ operands[6] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ operands[5] = operands[2];
+ operands[8] = operands[7] = operands[0];
+ }
+ /* Save the operand2 in a temporary location and use it. */
+ else if ((H_REG_P (operands[2])
+ || reg_mentioned_p (operands[0], operands[2]))
+ && !(SP_REG_P (operands[2]) && GET_CODE (operands[3]) == PLUS))
+ {
+ if (GET_CODE (operands[3]) == MINUS
+ && reg_mentioned_p (operands[0], operands[2]))
+ {
+ operands[9] = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ operands[1] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ operands[8] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[4] = operands[7] = operands[0];
+ operands[6] = operands[8];
+ operands[5] = operands[2];
+ }
+ else
+ {
+ operands[4] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[6] = operands[4];
+ if (!H_REG_P (operands[2]))
+ {
+ operands[5] = operands[0];
+ operands[7] = operands[2];
+ operands[8] = operands[0];
+ }
+ else
+ {
+ operands[5] = operands[2];
+ operands[8] = operands[7] = operands[0];
+ }
+ }
+ }
+ else
+ {
+ operands[4] = operands[5] = operands[0];
+ operands[6] = operands[2];
+ operands[8] = operands[7] = operands[0];
+ }
+ ")
+
+(define_split
+ [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
+ (match_operator:HI 3 "m68hc11_arith_operator"
+ [(match_operand:HI 1 "general_operand" "")
+ (match_operand:HI 2 "general_operand" "")]))]
+ "z_replacement_completed == 2
+ /* If we are adding a small constant to X or Y, it's
+ better to use one or several inx/iny instructions. */
+ && !(GET_CODE (operands[3]) == PLUS
+ && ((TARGET_M6812
+ && (immediate_operand (operands[2], HImode)
+ || hard_reg_operand (operands[2], HImode)))
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) >= -4
+ && INTVAL (operands[2]) <= 4)))"
+ [(set (match_dup 0) (match_dup 1))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 2)]))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "
+ ")
+
+;;
+;; Next split handles the logical operations on D register with
+;; another hard register for the second operand. For this, we
+;; have to save the second operand in a scratch location and use
+;; it instead. This must be supported because in some (rare) cases
+;; the second operand can come in a hard register and the reload
+;; pass doesn't know how to reload it in a memory location.
+;;
+;; PLUS MINUS AND IOR XOR
+;;
+;; The shift operators are special and must not appear here.
+;;
+(define_split
+ [(set (match_operand:HI 0 "d_register_operand" "")
+ (match_operator:HI 3 "m68hc11_non_shift_operator"
+ [(match_operand:HI 1 "d_register_operand" "")
+ (match_operand:HI 2 "hard_reg_operand" "")]))]
+ "TARGET_M6811
+ && z_replacement_completed == 2 && !SP_REG_P (operands[2])"
+ [(set (match_dup 4) (match_dup 2))
+ (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
+ "operands[4] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);")
+
+;;
+;; For 68HC12, push the operand[2] value on the stack and do the
+;; logical/arithmetic operation with a pop.
+;;
+(define_split
+ [(set (match_operand:HI 0 "d_register_operand" "")
+ (match_operator:HI 3 "m68hc11_non_shift_operator"
+ [(match_operand:HI 1 "d_register_operand" "")
+ (match_operand:HI 2 "hard_reg_operand" "")]))]
+ "TARGET_M6812
+ && z_replacement_completed == 2 && !SP_REG_P (operands[2])"
+ [(set (match_dup 4) (match_dup 2))
+ (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 5)]))]
+ "operands[4] = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ operands[5] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ ")
+
+;;--------------------------------------------------------------------
+;; 16-bit Unary operations on X and Y:
+;;
+;; NOT NEG
+;;
+;; Operations on X or Y registers are split here. Instructions are
+;; changed into:
+;; - xgdx/xgdy instruction pattern,
+;; - The same operation on register D,
+;; - xgdx/xgdy instruction pattern.
+;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
+;; We also handle the case were the address register is used in both source
+;; operands, such as:
+;;
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
+;; or
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
+;;
+(define_split
+ [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
+ (match_operator:HI 2 "m68hc11_unary_operator"
+ [(match_operand 1 "general_operand" "")]))]
+ "z_replacement_completed == 2"
+ [(set (match_dup 4) (match_dup 5))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM) (match_op_dup 2 [(match_dup 3)]))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "
+{
+ if ((H_REG_P (operands[1])
+ && !rtx_equal_p (operands[0], operands[1]))
+ || reg_mentioned_p (operands[0], operands[1]))
+ {
+ /* Move to the destination register, before the xgdx. */
+ operands[4] = gen_rtx_REG (GET_MODE (operands[1]),
+ REGNO (operands[0]));
+ operands[5] = operands[1];
+
+ /* Apply the operation on D. */
+ operands[3] = gen_rtx_REG (GET_MODE (operands[1]), HARD_D_REGNUM);
+ }
+ else
+ {
+ /* Generate a copy to same register (nop). */
+ operands[4] = operands[5] = operands[0];
+ operands[3] = operands[1];
+ }
+}")
+
+;;
+;; 8-bit operations on address registers.
+;;
+;; We have to take care that the address register is not used for the
+;; source of operand2. If operand2 is the D register, we have to save
+;; that register in a temporary location.
+;;
+;; AND OR XOR PLUS MINUS ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
+;;
+(define_split
+ [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (match_operator:QI 3 "m68hc11_arith_operator"
+ [(match_operand:QI 1 "hard_addr_reg_operand" "")
+ (match_operand:QI 2 "general_operand" "")]))]
+ "z_replacement_completed == 2
+ /* Reject a (plus:QI (reg:QI X) (const_int 1|-1)) because the
+ incqi pattern generates a better code. */
+ && !(GET_CODE (operands[3]) == PLUS
+ && GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == 1 || INTVAL (operands[2]) == -1))"
+ [(set (match_dup 5) (match_dup 6))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 4))
+ (set (match_dup 4) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (match_op_dup 3 [(reg:QI D_REGNUM) (match_dup 7)]))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 4))
+ (set (match_dup 4) (reg:HI D_REGNUM))])]
+ "operands[4] = gen_rtx_REG (HImode, REGNO (operands[0]));
+
+ /* For the second operand is a hard register or if the address
+ register appears in the source, we have to save the operand[2]
+ value in a temporary location and then use that temp.
+ Otherwise, it's ok and we generate a (set (D) (D)) that
+ will result in a nop. */
+ if (H_REG_P (operands[2]))
+ {
+ operands[5] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, REGNO (operands[2]));
+ operands[7] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ }
+ else if (reg_mentioned_p (operands[0], operands[2]))
+ {
+ operands[5] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ operands[6] = operands[2];
+ operands[7] = operands[5];
+ }
+ else
+ {
+ operands[5] = operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ operands[7] = operands[2];
+ }
+ ")
+
+;;
+;; Next split handles the logical operations on D register with
+;; another hard register for the second operand. For this, we
+;; have to save the second operand in a scratch location and use
+;; it instead. This must be supported because in some (rare) cases
+;; the second operand can come in a hard register and the reload
+;; pass doesn't know how to reload it in a memory location.
+;;
+;; PLUS MINUS AND IOR XOR
+;;
+;; The shift operators are special and must not appear here.
+;;
+(define_split
+ [(set (match_operand:QI 0 "d_register_operand" "")
+ (match_operator:QI 3 "m68hc11_non_shift_operator"
+ [(match_operand:QI 1 "d_register_operand" "")
+ (match_operand:QI 2 "hard_reg_operand" "")]))]
+ "reload_completed"
+ [(set (match_dup 5) (match_dup 6))
+ (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
+ "operands[4] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
+ operands[5] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+ operands[6] = gen_rtx_REG (HImode, REGNO (operands[2]));")
+
+;;--------------------------------------------------------------------
+;; 8-bit Unary operations on X and Y:
+;;
+;; NOT NEG
+;;
+;; Operations on X or Y registers are split here. Instructions are
+;; changed into:
+;; - xgdx/xgdy instruction pattern,
+;; - The same operation on register D,
+;; - xgdx/xgdy instruction pattern.
+;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
+;; We also handle the case were the address register is used in both source
+;; operands, such as:
+;;
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
+;; or
+;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
+;;
+(define_split
+ [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
+ (match_operator:QI 2 "m68hc11_unary_operator"
+ [(match_operand:QI 1 "general_operand" "")]))]
+ "z_replacement_completed == 2"
+ [(set (match_dup 4) (match_dup 5))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (match_op_dup 2 [(match_dup 6)]))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
+ (set (match_dup 3) (reg:HI D_REGNUM))])]
+ "
+{
+ operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
+ if ((H_REG_P (operands[1])
+ && !rtx_equal_p (operands[0], operands[1]))
+ || reg_mentioned_p (operands[0], operands[1]))
+ {
+ /* Move to the destination register, before the xgdx. */
+ operands[4] = operands[0];
+ operands[5] = operands[1];
+
+ /* Apply the operation on D. */
+ operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
+ }
+ else
+ {
+ operands[4] = operands[5] = operands[0];
+ operands[6] = operands[1];
+ }
+}")
+
+
+;;--------------------------------------------------------------------
+;;- Complements
+;;--------------------------------------------------------------------
+
+(define_expand "negdi2"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (neg:DI (match_operand:DI 1 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"__negdi2\", NEG, DImode, DImode, 2, operands);
+ DONE;")
+
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (neg:SI (match_operand:SI 1 "general_operand" "0")))]
+ ""
+ "*
+{
+ rtx ops[1];
+
+ CC_STATUS_INIT;
+
+ /* With -Os or without -O, use a special library call. */
+ if (optimize_size || optimize == 0)
+ return \"bsr\\t___negsi2\";
+
+ ops[0] = gen_label_rtx ();
+
+ /* 32-bit complement and add 1. */
+ output_asm_insn (\"comb\\n\\tcoma\\n\\txgdx\", operands);
+ output_asm_insn (\"comb\\n\\tcoma\\n\\tinx\\n\\txgdx\", operands);
+ output_asm_insn (\"bne\\t%l0\", ops);
+ output_asm_insn (\"inx\", operands);
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
+ return \"\";
+}")
+
+(define_insn "neghi2"
+ [(set (match_operand:HI 0 "register_operand" "=d,d,x*y")
+ (neg:HI (match_operand:HI 1 "general_operand" "0,!duim,0")))]
+ ""
+ "@
+ coma\\n\\tcomb\\n\\taddd\\t#1
+ clra\\n\\tclrb\\n\\tsubd\\t%1
+ xgd%0\\n\\tcoma\\n\\tcomb\\n\\txgd%0\\n\\tin%0")
+
+(define_insn "negqi2"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*A")
+ (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")))]
+ ""
+ "@
+ negb
+ neg\\t%b0
+ neg\\t%b0
+ #")
+
+;;
+;; - 32-bit complement. GCC knows how to translate them but providing a
+;; pattern generates better/smaller code.
+;;
+(define_expand "one_cmpldi2"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (not:DI (match_operand:DI 1 "general_operand" "")))]
+ ""
+ "m68hc11_emit_libcall (\"___notdi2\", NOT, DImode, DImode, 2, operands);
+ DONE;")
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,m,!u")
+ (not:SI (match_operand:SI 1 "general_operand" "0,m,0")))
+ (clobber (match_scratch:HI 2 "=X,d,X"))]
+ ""
+ "@
+ bsr\\t___one_cmplsi2
+ #
+ #")
+
+(define_insn "one_cmplhi2"
+ [(set (match_operand:HI 0 "non_push_operand" "=d,m,*A,u")
+ (not:HI (match_operand:HI 1 "general_operand" "0,0,0,0")))]
+ ""
+ "@
+ comb\\n\\tcoma
+ com\\t%b0\\n\\tcom\\t%h0
+ #
+ com\\t%b0\\n\\tcom\\t%h0")
+
+(define_insn "one_cmplqi2"
+ [(set (match_operand:QI 0 "non_push_operand" "=d,m,*A,u")
+ (not:QI (match_operand:QI 1 "general_operand" "0,0,0,0")))]
+ ""
+ "@
+ comb
+ com\\t%b0
+ #
+ com\\t%b0")
+
+(define_split /* "*one_cmplsi2" */
+ [(set (match_operand:SI 0 "non_push_operand" "")
+ (not:SI (match_dup 0)))
+ (clobber (match_scratch:HI 1 ""))]
+ "z_replacement_completed == 2
+ && (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
+ [(set (match_dup 2) (not:HI (match_dup 2)))
+ (set (match_dup 3) (not:HI (match_dup 3)))]
+ "operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
+ operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")
+
+(define_split /* "*one_cmplsi2" */
+ [(set (match_operand:SI 0 "non_push_operand" "")
+ (not:SI (match_operand:SI 1 "non_push_operand" "")))
+ (clobber (match_operand:HI 2 "d_register_operand" ""))]
+ "z_replacement_completed == 2
+ && (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 2) (not:HI (match_dup 2)))
+ (set (match_dup 4) (match_dup 2))
+ (set (match_dup 2) (match_dup 5))
+ (set (match_dup 2) (not:HI (match_dup 2)))
+ (set (match_dup 6) (match_dup 2))]
+ "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
+ operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[0]);")
+
+;;--------------------------------------------------------------------
+;;- arithmetic shifts
+;;--------------------------------------------------------------------
+;;
+;; Provide some 64-bit shift patterns.
+(define_expand "ashldi3"
+ [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (ashift:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 ""))])]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) != 1))
+ {
+ FAIL;
+ }
+}")
+
+(define_insn_and_split "*ashldi3_const32"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
+ (ashift:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
+ (const_int 32)))
+ (clobber (match_scratch:HI 2 "=&A,d,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "/* Move the lowpart in the highpart first in case the shift
+ is applied on the source. */
+ if (IS_STACK_PUSH (operands[0]))
+ {
+ m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
+ const0_rtx, operands[2]);
+
+ /* Adjust first operand if it uses SP so that we take into
+ account the above push. Can occur only for 68HC12. */
+ if (reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM),
+ operands[1]))
+ operands[1] = adjust_address (operands[1],
+ GET_MODE (operands[0]), 4);
+ }
+ m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
+ m68hc11_gen_lowpart (SImode, operands[1]),
+ operands[2]);
+ if (!IS_STACK_PUSH (operands[0]))
+ {
+ m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
+ const0_rtx, operands[2]);
+ }
+ DONE;")
+
+(define_insn_and_split "*ashldi3_const1"
+ [(set (match_operand:DI 0 "non_push_operand" "=m,m,u")
+ (ashift:DI (match_operand:DI 1 "general_operand" "mi,u,umi")
+ (const_int 1)))
+ (clobber (match_scratch:HI 2 "=d,d,d"))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 2) (ashift:HI (match_dup 2) (const_int 1)))
+ (set (match_dup 4) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 5))
+ (parallel [(set (match_dup 2)
+ (rotate:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 6) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 7))
+ (parallel [(set (match_dup 2)
+ (rotate:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 8) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 9))
+ (parallel [(set (match_dup 2)
+ (rotate:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 10) (match_dup 2))]
+ "operands[3] = m68hc11_gen_lowpart (SImode, operands[1]);
+ operands[5] = m68hc11_gen_highpart (HImode, operands[3]);
+ operands[3] = m68hc11_gen_lowpart (HImode, operands[3]);
+
+ operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
+
+ operands[7] = m68hc11_gen_highpart (SImode, operands[1]);
+ operands[9] = m68hc11_gen_highpart (HImode, operands[7]);
+ operands[7] = m68hc11_gen_lowpart (HImode, operands[7]);
+
+ operands[8] = m68hc11_gen_highpart (SImode, operands[0]);
+ operands[10] = m68hc11_gen_highpart (HImode, operands[8]);
+ operands[8] = m68hc11_gen_lowpart (HImode, operands[8]);")
+
+(define_insn "addsi_silshr16"
+ [(set (match_operand:SI 0 "register_operand" "=D,D,!D")
+ (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "!*uim,0,0")
+ (const_int 16))
+ (match_operand:SI 2 "general_operand" "0,m!*u,0")))]
+ ""
+ "#")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
+ (const_int 16))
+ (match_operand:SI 2 "general_operand" "")))]
+ "z_replacement_completed == 2 && !X_REG_P (operands[1])"
+ [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
+ (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
+ (const_int 0))
+ (reg:HI CC_REGNUM)))]
+ "operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
+ (const_int 16))
+ (match_operand:SI 2 "general_operand" "")))]
+ "z_replacement_completed == 2 && X_REG_P (operands[1])"
+ [(set (reg:HI D_REGNUM) (match_dup 5))
+ (set (reg:HI X_REGNUM) (match_dup 3))
+ (set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 4)))
+ (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
+ (const_int 0))
+ (reg:HI CC_REGNUM)))]
+ "operands[3] = m68hc11_gen_highpart (HImode, operands[2]);
+ if (X_REG_P (operands[2]))
+ {
+ operands[4] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ operands[5] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ }
+ else
+ {
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
+ operands[5] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ }
+")
+
+(define_insn "addsi_ashift16"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (plus:SI
+ (mult:SI (match_operand:SI 2 "general_operand" "uim")
+ (const_int 65536))
+ (match_operand:SI 1 "general_operand" "0")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "0"
+ "#")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI
+ (mult:SI (match_operand:SI 2 "general_operand" "")
+ (const_int 65536))
+ (match_operand:SI 1 "general_operand" "")))
+ (clobber (match_scratch:HI 3 "=X"))]
+ "0 && reload_completed && z_replacement_completed == 2"
+ [(set (reg:HI X_REGNUM) (plus:HI (reg:HI X_REGNUM) (match_dup 4)))]
+ "
+{
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
+}")
+
+(define_insn_and_split "addsi_andshr16"
+ [(set (match_operand:SI 0 "register_operand" "=D")
+ (plus:SI (and:SI (match_operand:SI 1 "general_operand" "%uim")
+ (const_int 65535))
+ (match_operand:SI 2 "general_operand" "0")))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
+ (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM) (const_int 0)) (reg:HI CC_REGNUM)))]
+ "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")
+
+;;
+;; 32-bit shifts are made by a small library routine that uses
+;; a specific passing convention for parameters (for efficiency reasons).
+;;
+;; [D + X] -> Value to be shifted
+;; Y -> Shift count
+;;
+;; The shift count is clobbered by the routine.
+;;
+(define_expand "ashlsi3"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "general_operand" ""))
+ (clobber (scratch:HI))])
+ (parallel
+ [(set (match_dup 0) (ashift:SI (match_dup 0)
+ (match_operand:HI 2 "nonmemory_operand" "")))
+ (clobber (scratch:HI))])]
+ ""
+ "")
+
+(define_split
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (ashift:SI (match_operand:SI 1 "general_operand" "")
+ (const_int 16)))
+ (clobber (match_scratch:HI 3 ""))]
+ ""
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (const_int 0))]
+ "operands[2] = m68hc11_gen_highpart (HImode, operands[0]);
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
+ operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")
+
+(define_insn "*ashlsi3_const16"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,*u")
+ (ashift:SI (match_operand:SI 1 "general_operand" "Duim,D,D")
+ (const_int 16)))
+ (clobber (match_scratch:HI 2 "=X,X,X"))]
+ ""
+ "#")
+
+(define_insn_and_split "*ashlsi3_const16_zexthi"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=D")
+ (ashift:SI (zero_extend:HI
+ (match_operand:HI 1 "general_operand" "duim*A"))
+ (const_int 16)))
+ (clobber (match_scratch:HI 2 "=X"))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (reg:HI X_REGNUM) (match_dup 1))
+ (set (reg:HI D_REGNUM) (const_int 0))]
+ "")
+
+(define_insn "*ashlsi3_const1"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,D,D,m,*u,*u")
+ (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,m,*u,m,*u,m")
+ (const_int 1)))
+ (clobber (match_scratch:HI 2 "=X,X,X,&d,&d,&d"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (X_REG_P (operands[1]))
+ {
+ return \"lsld\\n\\txgdx\\n\\trolb\\n\\trola\\n\\txgdx\";
+ }
+ else
+ {
+ rtx ops[2];
+
+ ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"lsld\", ops);
+ if (!X_REG_P (operands[0]))
+ {
+ ops[1] = ops[0];
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
+ m68hc11_gen_movhi (insn, ops);
+ ops[0] = ops[1];
+ ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
+ m68hc11_gen_movhi (insn, ops);
+ }
+ else
+ {
+ /* Load the high part in X in case the source operand
+ uses X as a memory pointer. */
+ ops[0] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"xgdx\", ops);
+ }
+ output_asm_insn (\"rolb\", ops);
+ output_asm_insn (\"rola\", ops);
+ if (!X_REG_P (operands[0]))
+ {
+ ops[1] = ops[0];
+ ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
+ m68hc11_gen_movhi (insn, ops);
+ }
+ else
+ {
+ output_asm_insn (\"xgdx\", ops);
+ }
+ return \"\";
+ }
+}")
+
+(define_insn "*ashlsi3_const"
+ [(set (match_operand:SI 0 "register_operand" "+D")
+ (ashift:SI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (clobber (match_scratch:HI 2 "=y"))]
+ "TARGET_M6811 /* See *ashlsi3 note. */"
+ "*
+{
+ CC_STATUS_INIT;
+ return \"ldy\\t%1\\n\\tbsr\\t___ashlsi3\";
+}")
+
+(define_insn "*ashlsi3"
+ [(set (match_operand:SI 0 "register_operand" "+D,D")
+ (ashift:SI (match_dup 0)
+ (match_operand:HI 1 "general_operand" "y,mi")))
+ (clobber (match_scratch:HI 2 "=1,X"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+
+ /* There is a reload problem if we don't accept 'm' for the shift value.
+ A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
+ and this conflicts with all reloads. Since X, Y, Z are used there
+ is not enough register in class A_REGS.
+
+ Assuming that 'operands[1]' does not refer to the stack (which
+ is true for 68hc11 only, we save temporary the value of Y.
+
+ For 68HC12 we must also accept a constant because Z register is
+ disabled when compiling with -fomit-frame-pointer. We can come up
+ with a reload problem and the *lshrsi3_const pattern was disabled
+ for that reason. */
+ if (!Y_REG_P (operands[2]))
+ {
+ rtx ops[1];
+ int y_dead = dead_register_here (insn, iy_reg);
+
+ ops[0] = operands[1];
+ if (y_dead == 0)
+ {
+ output_asm_insn (\"pshy\", operands);
+ if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
+ ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
+ }
+ output_asm_insn (\"ldy\\t%0\", ops);
+ output_asm_insn (\"bsr\\t___ashlsi3\", operands);
+ return y_dead == 0 ? \"puly\" : \"\";
+ }
+ return \"bsr\\t___ashlsi3\";
+}")
+
+(define_expand "ashlhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (ashift:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx scratch = gen_reg_rtx (HImode);
+ emit_move_insn (scratch, operands[2]);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_ASHIFT (HImode,
+ operand1, scratch)),
+ gen_rtx_CLOBBER (VOIDmode, scratch))));
+ DONE;
+ }
+}")
+
+(define_insn "*ashlhi3_const1"
+ [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
+ (ashift:HI (match_operand:HI 1 "non_push_operand" "0,0")
+ (const_int 1)))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (D_REG_P (operands[0]))
+ {
+ return \"asld\";
+ }
+
+ output_asm_insn (\"asl\\t%b0\", operands);
+ output_asm_insn (\"rol\\t%h0\", operands);
+ CC_STATUS_INIT;
+ return \"\";
+}")
+
+
+(define_insn "*ashlhi3_2"
+ [(set (match_operand:HI 0 "register_operand" "=d,*x")
+ (ashift:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "register_operand" "+x,+d")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ return \"bsr\\t___lshlhi3\";
+}")
+
+(define_insn "*ashlhi3"
+ [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
+ (ashift:HI (match_dup 0)
+ (match_operand:HI 1 "register_operand" "+x")))
+ (clobber (match_dup 1))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ return \"bsr\\t___lshlhi3\";
+}")
+
+(define_insn "*ashlhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,!*A")
+ (ashift:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int i;
+
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ i = INTVAL (operands[2]);
+ if (i >= 8)
+ {
+ CC_STATUS_INIT;
+ output_asm_insn (\"tba\", operands);
+ if (i == 15)
+ {
+ output_asm_insn (\"rora\", operands);
+ output_asm_insn (\"anda\\t#0\", operands);
+ output_asm_insn (\"rora\", operands);
+ }
+ else
+ while (i != 8 )
+ {
+ output_asm_insn (\"asla\", operands);
+ i--;
+ }
+ return \"clrb\";
+ }
+ for (i = 0; i < INTVAL (operands[2]) - 1; i++)
+ {
+ output_asm_insn (\"asld\", operands);
+ }
+ return \"asld\";
+}")
+
+(define_expand "ashlqi3"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (ashift:QI (match_operand:QI 1 "register_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*ashlqi3_const1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
+ (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
+ (const_int 1)))]
+ ""
+ "@
+ aslb
+ asl\\t%b0
+ asl\\t%b0
+ asl%0
+ #")
+
+(define_insn "*ashlqi3_const"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int i;
+ const char* insn_code;
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ insn_code = \"aslb\";
+ else if (DA_REG_P (operands[0]))
+ insn_code = \"asla\";
+ else
+ return \"#\";
+
+ i = INTVAL (operands[2]);
+ if (i >= 8)
+ {
+ if (DA_REG_P (operands[0]))
+ return \"clra\";
+ else
+ return \"clrb\";
+ }
+ else if (i == 7)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ output_asm_insn (\"rora\", operands);
+ output_asm_insn (\"ldaa\\t#0\", operands);
+ return \"rora\";
+ }
+ else
+ {
+ output_asm_insn (\"rorb\", operands);
+ output_asm_insn (\"ldab\\t#0\", operands);
+ return \"rorb\";
+ }
+ }
+ else if (i == 6)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ output_asm_insn (\"rora\", operands);
+ output_asm_insn (\"rora\", operands);
+ output_asm_insn (\"rora\", operands);
+ return \"anda\\t#0xC0\";
+ }
+ else
+ {
+ output_asm_insn (\"rorb\", operands);
+ output_asm_insn (\"rorb\", operands);
+ output_asm_insn (\"rorb\", operands);
+ return \"andb\\t#0xC0\";
+ }
+ }
+ while (--i >= 0)
+ {
+ output_asm_insn (insn_code, operands);
+ }
+ return \"\";
+}")
+
+(define_insn "*ashlqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "nonimmediate_operand"
+ "m*u*d*A,m*u*d*A,m*u")))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
+ return \"#\";
+
+ ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+
+ CC_STATUS_INIT;
+ return \"bsr\\t___lshlqi3\";
+}")
+
+(define_expand "ashrhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (ashiftrt:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx scratch = gen_reg_rtx (HImode);
+
+ emit_move_insn (scratch, operands[2]);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_ASHIFTRT (HImode,
+ operand1, scratch)),
+ gen_rtx_CLOBBER (VOIDmode, scratch))));
+ DONE;
+ }
+}")
+
+(define_insn "*ashrhi3_const1"
+ [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
+ (ashiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
+ (const_int 1)))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ if (D_REG_P (operands[0]))
+ {
+ return \"asra\\n\\trorb\";
+ }
+
+ output_asm_insn (\"asr\\t%h0\", operands);
+ output_asm_insn (\"ror\\t%b0\", operands);
+ return \"\";
+}")
+
+
+(define_insn "*ashrhi3_const"
+ [(set (match_operand:HI 0 "register_operand" "=d,!*A")
+ (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ rtx ops[2];
+ int val = INTVAL (operands[2]);
+
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (val >= 15)
+ {
+ ops[0] = gen_label_rtx ();
+
+ output_asm_insn (\"clrb\", operands);
+ output_asm_insn (\"rola\", operands);
+
+ /* Clear A without clearing the carry flag. */
+ output_asm_insn (\"tba\", operands);
+ output_asm_insn (\"bcc\\t%l0\", ops);
+ output_asm_insn (\"coma\", operands);
+ output_asm_insn (\"comb\", operands);
+
+ CC_STATUS_INIT;
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+ return \"\";
+ }
+ if (val >= 8)
+ {
+ ops[0] = gen_label_rtx ();
+
+ output_asm_insn (\"tab\", operands);
+ output_asm_insn (\"clra\", operands);
+ output_asm_insn (\"tstb\", operands);
+ output_asm_insn (\"bge\\t%l0\", ops);
+ output_asm_insn (\"deca\", operands);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+
+ val -= 8;
+
+ while (val > 0)
+ {
+ output_asm_insn (\"asrb\", operands);
+ val--;
+ }
+ /* Status is ok. */
+ return \"\";
+ }
+ if (val == 7)
+ {
+ ops[0] = gen_label_rtx ();
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"tab\", operands);
+ output_asm_insn (\"anda\\t#0\", operands);
+ output_asm_insn (\"bcc\\t%l0\", ops);
+ output_asm_insn (\"coma\", ops);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+ return \"\";
+ }
+ while (val > 0)
+ {
+ output_asm_insn (\"asra\", operands);
+ output_asm_insn (\"rorb\", operands);
+ val--;
+ }
+ CC_STATUS_INIT;
+
+ return \"\";
+}")
+
+(define_insn "*ashrhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,*x")
+ (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "register_operand" "+x,+d")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ output_asm_insn (\"bsr\\t___ashrhi3\", operands);
+ return \"\";
+}")
+
+(define_expand "ashrsi3"
+ [(parallel
+ [(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
+ (clobber (scratch:HI))])
+ (parallel
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ashiftrt:SI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (scratch:HI))])]
+ ""
+ "")
+
+(define_insn "*ashrsi3_const"
+ [(set (match_operand:SI 0 "register_operand" "+D")
+ (ashiftrt:SI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (clobber (match_scratch:HI 2 "=y"))]
+ "TARGET_M6811 /* See *ashrsi3 note. */"
+ "*
+{
+ CC_STATUS_INIT;
+ return \"ldy\\t%1\\n\\tbsr\\t___ashrsi3\";
+}")
+
+(define_insn "*ashrsi3"
+ [(set (match_operand:SI 0 "register_operand" "+D,D")
+ (ashiftrt:SI (match_dup 0)
+ (match_operand:HI 1 "general_operand" "y,mi")))
+ (clobber (match_scratch:HI 2 "=1,X"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ /* There is a reload problem if we don't accept 'm' for the shift value.
+ A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
+ and this conflicts with all reloads. Since X, Y, Z are used there
+ is not enough register in class A_REGS.
+
+ Assuming that 'operands[1]' does not refer to the stack (which
+ is true for 68hc11 only, we save temporary the value of Y.
+
+ For 68HC12 we must also accept a constant because Z register is
+ disabled when compiling with -fomit-frame-pointer. We can come up
+ with a reload problem and the *lshrsi3_const pattern was disabled
+ for that reason. */
+ if (!Y_REG_P (operands[2]))
+ {
+ rtx ops[1];
+ int y_dead = dead_register_here (insn, iy_reg);
+
+ ops[0] = operands[1];
+ if (y_dead == 0)
+ {
+ output_asm_insn (\"pshy\", operands);
+ if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
+ ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
+ }
+ output_asm_insn (\"ldy\\t%0\", ops);
+ output_asm_insn (\"bsr\\t___ashrsi3\", operands);
+ return y_dead == 0 ? \"puly\" : \"\";
+ }
+ return \"bsr\\t___ashrsi3\";
+}")
+
+(define_expand "ashrqi3"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (ashiftrt:QI (match_operand:QI 1 "register_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*ashrqi3_const1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
+ (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
+ (const_int 1)))]
+ ""
+ "@
+ asrb
+ asr\\t%b0
+ asr\\t%b0
+ asr%0
+ #")
+
+(define_insn "*ashrqi3_const"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int i;
+ const char* insn_code;
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ insn_code = \"asrb\";
+ else if (DA_REG_P (operands[0]))
+ insn_code = \"asra\";
+ else
+ return \"#\";
+
+ i = INTVAL (operands[2]);
+ if (i > 8)
+ i = 8;
+ while (--i >= 0)
+ {
+ output_asm_insn (insn_code, operands);
+ }
+ return \"\";
+}")
+
+(define_insn "*ashrqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "nonimmediate_operand"
+ "m*u*d*A,m*u*d*A,m*u")))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
+ return \"#\";
+
+ ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+
+ CC_STATUS_INIT;
+ return \"bsr\\t___ashrqi3\";
+}")
+
+;;--------------------------------------------------------------------
+;; logical shift instructions
+;;--------------------------------------------------------------------
+(define_expand "lshrdi3"
+ [(parallel [(set (match_operand:DI 0 "general_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (match_scratch:HI 3 ""))])]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) < 48
+ && INTVAL (operands[2]) != 1))
+ {
+ FAIL;
+ }
+}")
+
+(define_insn_and_split "*lshrdi3_const32"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
+ (const_int 32)))
+ (clobber (match_scratch:HI 2 "=&A,d,d"))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
+ m68hc11_gen_highpart (SImode, operands[1]),
+ operands[2]);
+ m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
+ const0_rtx, operands[2]);
+ DONE;")
+
+(define_insn "*lshrdi3_const63"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=m,u")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
+ (match_operand:DI 2 "const_int_operand" "")))
+ (clobber (match_scratch:HI 3 "=d,d"))]
+ "INTVAL (operands[2]) >= 48"
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:DI 2 "const_int_operand" "")))
+ (clobber (match_scratch:HI 3 "=d"))]
+ "z_replacement_completed && INTVAL (operands[2]) >= 56"
+ [(set (reg:QI D_REGNUM) (match_dup 9))
+ (set (reg:QI D_REGNUM) (lshiftrt:QI (reg:QI D_REGNUM) (match_dup 8)))
+ (set (reg:HI D_REGNUM) (zero_extend:HI (reg:QI D_REGNUM)))
+ (set (match_dup 4) (reg:HI D_REGNUM))
+ (set (reg:QI D_REGNUM) (const_int 0))
+ (set (match_dup 5) (reg:HI D_REGNUM))
+ (set (match_dup 6) (reg:HI D_REGNUM))
+ (set (match_dup 7) (reg:HI D_REGNUM))]
+ "operands[8] = GEN_INT (INTVAL (operands[2]) - 56);
+ operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
+ operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
+
+ operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
+ operands[9] = m68hc11_gen_highpart (HImode, operands[9]);
+ operands[9] = m68hc11_gen_highpart (QImode, operands[9]);
+
+ operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
+ operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
+ operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")
+
+(define_split
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "")
+ (match_operand:DI 2 "const_int_operand" "")))
+ (clobber (match_scratch:HI 3 "=d"))]
+ "z_replacement_completed && INTVAL (operands[2]) >= 48
+ && INTVAL (operands[2]) < 56"
+ [(set (reg:HI D_REGNUM) (match_dup 9))
+ (set (reg:HI D_REGNUM) (lshiftrt:HI (reg:HI D_REGNUM) (match_dup 8)))
+ (set (match_dup 4) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (const_int 0))
+ (set (match_dup 5) (reg:HI D_REGNUM))
+ (set (match_dup 6) (reg:HI D_REGNUM))
+ (set (match_dup 7) (reg:HI D_REGNUM))]
+ "operands[8] = GEN_INT (INTVAL (operands[2]) - 48);
+ operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
+ operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
+ operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
+
+ operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
+ operands[9] = m68hc11_gen_highpart (HImode, operands[1]);
+ operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
+ operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
+ operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")
+
+(define_insn_and_split "*lshrdi_const1"
+ [(set (match_operand:DI 0 "non_push_operand" "=m,u")
+ (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
+ (const_int 1)))
+ (clobber (match_scratch:HI 2 "=d,d"))]
+ ""
+ "#"
+ "z_replacement_completed == 2"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 2) (lshiftrt:HI (match_dup 2) (const_int 1)))
+ (set (match_dup 4) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 5))
+ (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 6) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 7))
+ (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 8) (match_dup 2))
+
+ (set (match_dup 2) (match_dup 9))
+ (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 10) (match_dup 2))]
+ "operands[3] = m68hc11_gen_highpart (SImode, operands[1]);
+ operands[5] = m68hc11_gen_lowpart (HImode, operands[3]);
+ operands[3] = m68hc11_gen_highpart (HImode, operands[3]);
+
+ operands[4] = m68hc11_gen_highpart (SImode, operands[0]);
+ operands[6] = m68hc11_gen_lowpart (HImode, operands[4]);
+ operands[4] = m68hc11_gen_highpart (HImode, operands[4]);
+
+ operands[7] = m68hc11_gen_lowpart (SImode, operands[1]);
+ operands[9] = m68hc11_gen_lowpart (HImode, operands[7]);
+ operands[7] = m68hc11_gen_highpart (HImode, operands[7]);
+
+ operands[8] = m68hc11_gen_lowpart (SImode, operands[0]);
+ operands[10] = m68hc11_gen_lowpart (HImode, operands[8]);
+ operands[8] = m68hc11_gen_highpart (HImode, operands[8]);")
+
+(define_expand "lshrsi3"
+ [(parallel
+ [(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
+ (clobber (scratch:HI))])
+ (parallel
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lshiftrt:SI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))
+ (clobber (scratch:HI))])]
+ ""
+ "")
+
+(define_split
+ [(set (match_operand:SI 0 "non_push_operand" "")
+ (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
+ (const_int 16)))
+ (clobber (match_scratch:HI 3 ""))]
+ "reload_completed && !(X_REG_P (operands[0]) && X_REG_P (operands[1]))"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (const_int 0))]
+ "operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
+ operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
+ operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")
+
+(define_insn "*lshrsi3_const16"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,D,m,u")
+ (lshiftrt:SI (match_operand:SI 1 "general_operand" "uim,0,D,D")
+ (const_int 16)))
+ (clobber (match_scratch:HI 2 "=X,X,X,X"))]
+ ""
+ "@
+ #
+ xgdx\\n\\tldx\\t#0
+ #
+ #")
+
+(define_insn "*lshrsi3_const1"
+ [(set (match_operand:SI 0 "non_push_operand" "=D,D,D,m,*u,*u")
+ (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,m,*u,m,*u,m")
+ (const_int 1)))
+ (clobber (match_scratch:HI 2 "=X,X,X,&d,&d,&d"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (X_REG_P (operands[1]))
+ {
+ return \"xgdx\\n\\tlsrd\\n\\txgdx\\n\\trora\\n\\trorb\";
+ }
+ else
+ {
+ rtx ops[2];
+
+ ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
+ ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"lsrd\", ops);
+ if (!X_REG_P (operands[0]))
+ {
+ ops[1] = ops[0];
+ ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
+ m68hc11_gen_movhi (insn, ops);
+ ops[0] = ops[1];
+ ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
+ m68hc11_gen_movhi (insn, ops);
+ }
+ else
+ {
+ /* Load the lowpart in X in case the operands is some N,x. */
+ ops[0] = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"xgdx\", ops);
+ }
+ output_asm_insn (\"rora\", ops);
+ output_asm_insn (\"rorb\", ops);
+ if (!X_REG_P (operands[0]))
+ {
+ ops[1] = ops[0];
+ ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
+ m68hc11_gen_movhi (insn, ops);
+ }
+ return \"\";
+ }
+}")
+
+(define_insn "*lshrsi3_const"
+ [(set (match_operand:SI 0 "register_operand" "+D")
+ (lshiftrt:SI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (clobber (match_scratch:HI 2 "=y"))]
+ "TARGET_M6811 /* See *lshrsi3 note. */"
+ "*
+{
+ CC_STATUS_INIT;
+ return \"ldy\\t%1\\n\\tbsr\\t___lshrsi3\";
+}")
+
+(define_insn "*lshrsi3"
+ [(set (match_operand:SI 0 "register_operand" "+D,D")
+ (lshiftrt:SI (match_dup 0)
+ (match_operand:HI 1 "general_operand" "y,mi")))
+ (clobber (match_scratch:HI 2 "=1,X"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ /* There is a reload problem if we don't accept 'm' for the shift value.
+ A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
+ and this conflicts with all reloads. Since X, Y, Z are used there
+ is not enough register in class A_REGS.
+
+ Assuming that 'operands[1]' does not refer to the stack (which
+ is true for 68hc11 only, we save temporary the value of Y.
+
+ For 68HC12 we must also accept a constant because Z register is
+ disabled when compiling with -fomit-frame-pointer. We can come up
+ with a reload problem and the *lshrsi3_const pattern was disabled
+ for that reason. */
+ if (!Y_REG_P (operands[2]))
+ {
+ rtx ops[1];
+ int y_dead = dead_register_here (insn, iy_reg);
+
+ ops[0] = operands[1];
+ if (y_dead == 0)
+ {
+ output_asm_insn (\"pshy\", operands);
+ if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
+ ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
+ }
+ output_asm_insn (\"ldy\\t%0\", ops);
+ output_asm_insn (\"bsr\\t___lshrsi3\", operands);
+ return y_dead == 0 ? \"puly\" : \"\";
+ }
+ return \"bsr\\t___lshrsi3\";
+}")
+
+(define_expand "lshrhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (lshiftrt:HI (match_operand:HI 1 "general_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx scratch = gen_reg_rtx (HImode);
+ operand1 = force_reg (HImode, operand1);
+
+ emit_move_insn (scratch, operands[2]);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_LSHIFTRT (HImode,
+ operand1, scratch)),
+ gen_rtx_CLOBBER (VOIDmode, scratch))));
+ DONE;
+ }
+}")
+
+(define_insn "lshrhi3_const1"
+ [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
+ (lshiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
+ (const_int 1)))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (D_REG_P (operands[0]))
+ return \"lsrd\";
+
+ CC_STATUS_INIT;
+ return \"lsr\\t%h0\\n\\tror\\t%b0\";
+}")
+
+(define_insn "lshrhi3_const"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,!*A,!*A")
+ (lshiftrt:HI (match_operand:HI 1 "general_operand" "dm*A,!u,dm,!u")
+ (match_operand:HI 2 "const_int_operand" "i,i,i,i")))]
+ ""
+ "*
+{
+ int val = INTVAL (operands[2]);
+
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ if (val >= 8)
+ {
+ if (val == 8)
+ CC_STATUS_INIT;
+
+ if (!H_REG_P (operands[1]))
+ {
+ output_asm_insn (\"clra\", operands);
+ output_asm_insn (\"ldab\\t%h1\", operands);
+ }
+ else if (A_REG_P (operands[1]))
+ {
+ output_asm_insn (\"st%1\\t%t0\", operands);
+ output_asm_insn (\"ldab\\t%t0\", operands);
+ output_asm_insn (\"clra\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"tab\", operands);
+ output_asm_insn (\"clra\", operands);
+ }
+ val -= 8;
+ switch (val)
+ {
+ case 7:
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"tab\", operands);
+ output_asm_insn (\"rolb\", operands);
+ break;
+
+ case 6:
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"andb\\t#3\", operands);
+ break;
+
+ default:
+ while (val > 0)
+ {
+ val --;
+ output_asm_insn (\"lsrb\", operands);
+ }
+ break;
+ }
+ return \"\";
+ }
+
+ if (!D_REG_P (operands[1]))
+ m68hc11_gen_movhi (insn, operands);
+ switch (val)
+ {
+ case 7:
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"tab\", operands);
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"anda\\t#1\", operands);
+ CC_STATUS_INIT;
+ break;
+
+ default:
+ while (val > 0)
+ {
+ val --;
+ output_asm_insn (\"lsrd\", operands);
+ }
+ }
+ return \"\";
+}")
+
+(define_insn "*lshrhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,*x")
+ (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "register_operand" "+x,+d")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ return \"bsr\\t___lshrhi3\";
+}")
+
+(define_expand "lshrqi3"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (lshiftrt:QI (match_operand:QI 1 "register_operand" "")
+ (match_operand:QI 2 "general_operand" "")))]
+ ""
+ "")
+
+(define_insn "*lshrqi3_const1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,d,!u,!*q,!*A")
+ (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
+ (const_int 1)))]
+ ""
+ "@
+ lsr\\t%b0
+ lsrb
+ lsr\\t%b0
+ lsr%0
+ #")
+
+(define_insn "*lshrqi3_const"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "const_int_operand" "")))]
+ ""
+ "*
+{
+ int i;
+ const char* insn_code;
+
+ if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+ insn_code = \"lsrb\";
+ else if (DA_REG_P (operands[0]))
+ insn_code = \"lsra\";
+ else
+ return \"#\";
+
+ i = INTVAL (operands[2]);
+ if (i >= 8)
+ {
+ if (DA_REG_P (operands[0]))
+ return \"clra\";
+ else
+ return \"clrb\";
+ }
+ else if (i == 7)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"ldaa\\t#0\", operands);
+ return \"rola\";
+ }
+ else
+ {
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"ldab\\t#0\", operands);
+ return \"rolb\";
+ }
+ }
+ else if (i == 6)
+ {
+ if (DA_REG_P (operands[0]))
+ {
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"rola\", operands);
+ output_asm_insn (\"rola\", operands);
+ return \"anda\\t#3\";
+ }
+ else
+ {
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rolb\", operands);
+ output_asm_insn (\"rolb\", operands);
+ return \"andb\\t#3\";
+ }
+ }
+ while (--i >= 0)
+ {
+ output_asm_insn (insn_code, operands);
+ }
+ return \"\";
+}")
+
+(define_insn "*lshrqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
+ (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
+ (match_operand:QI 2 "nonimmediate_operand"
+ "m*u*d*A,m*u*d*A,m*u")))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+
+ if (!optimize || optimize_size)
+ {
+ return \"bsr\\t___lshrqi3\";
+ }
+
+ ops[0] = gen_label_rtx ();
+ ops[1] = gen_label_rtx ();
+ output_asm_insn (\"ble\\t%l1\", ops);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[0]));
+
+ output_asm_insn (\"lsrb\", operands);
+ output_asm_insn (\"deca\", operands);
+ output_asm_insn (\"bne\\t%l0\", ops);
+
+ (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+ CODE_LABEL_NUMBER (ops[1]));
+ return \"\";
+}")
+
+(define_insn "*rotlqi3_with_carry"
+ [(set (match_operand:QI 0 "register_operand" "=d,!q")
+ (rotate:QI (match_operand:QI 1 "register_operand" "0,0")
+ (reg:QI CC_REGNUM)))]
+ ""
+ "*
+{
+ if (DA_REG_P (operands[0]))
+ return \"rola\";
+ else
+ return \"rolb\";
+}")
+
+(define_insn "*rotlhi3_with_carry"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (rotate:HI (match_operand:HI 1 "register_operand" "0")
+ (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ return \"rolb\\n\\trola\";
+}")
+
+(define_insn "*rotrhi3_with_carry"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (rotatert:HI (match_operand:HI 1 "register_operand" "0")
+ (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ return \"rora\\n\\trorb\";
+}")
+
+(define_insn "rotlqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,!q")
+ (rotate:QI (match_operand:QI 1 "register_operand" "0,0")
+ (match_operand:QI 2 "const_int_operand" "i,i")))]
+ ""
+ "*
+{
+ m68hc11_gen_rotate (ROTATE, insn, operands);
+ return \"\";
+}")
+
+(define_insn "rotrqi3"
+ [(set (match_operand:QI 0 "register_operand" "=d,!q")
+ (rotatert:QI (match_operand:QI 1 "register_operand" "0,0")
+ (match_operand:QI 2 "const_int_operand" "i,i")))]
+ ""
+ "*
+{
+ m68hc11_gen_rotate (ROTATERT, insn, operands);
+ return \"\";
+}")
+
+(define_expand "rotlhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (rotate:HI (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx scratch = gen_reg_rtx (HImode);
+ operand1 = force_reg (HImode, operand1);
+
+ emit_move_insn (scratch, operands[2]);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_ROTATE (HImode,
+ operand1, scratch)),
+ gen_rtx_CLOBBER (VOIDmode, scratch))));
+ DONE;
+ }
+}")
+
+(define_insn "rotlhi3_const"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (rotate:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "const_int_operand" "i")))]
+ ""
+ "*
+{
+ m68hc11_gen_rotate (ROTATE, insn, operands);
+ return \"\";
+}")
+
+(define_insn "*rotlhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,*x")
+ (rotate:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "+x,+d")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ return \"bsr\\t___rotlhi3\";
+}")
+
+(define_expand "rotrhi3"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (rotatert:HI (match_operand:HI 1 "general_operand" "")
+ (match_operand:HI 2 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx scratch = gen_reg_rtx (HImode);
+ operand1 = force_reg (HImode, operand1);
+
+ emit_move_insn (scratch, operands[2]);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (VOIDmode,
+ operand0,
+ gen_rtx_ROTATERT (HImode,
+ operand1, scratch)),
+ gen_rtx_CLOBBER (VOIDmode, scratch))));
+ DONE;
+ }
+}")
+
+(define_insn "rotrhi3_const"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (rotatert:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "const_int_operand" "i")))]
+ ""
+ "*
+{
+ m68hc11_gen_rotate (ROTATERT, insn, operands);
+ return \"\";
+}")
+
+(define_insn "*rotrhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,*x")
+ (rotatert:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "+x,+d")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ if (A_REG_P (operands[0]))
+ return \"#\";
+
+ return \"bsr\\t___rotrhi3\";
+}")
+
+;; Split a shift operation on an address register in a shift
+;; on D_REGNUM.
+(define_split /* "*rotrhi3_addr" */
+ [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
+ (match_operator:HI 3 "m68hc11_shift_operator"
+ [(match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "register_operand" "")]))
+ (clobber (match_dup 2))]
+ "z_replacement_completed == 2"
+ [(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (parallel [(set (reg:HI D_REGNUM)
+ (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 0)]))
+ (clobber (match_dup 0))])
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "")
+
+;;--------------------------------------------------------------------
+;;- 68HC12 Decrement/Increment and branch
+;;--------------------------------------------------------------------
+;; These patterns are used by loop optimization as well as peephole2
+;; They must handle reloading themselves and the scratch register
+;; is used for that. Even if we accept memory operand, we must not
+;; accept them on the predicate because it might create too many reloads.
+;; (specially on HC12 due to its auto-incdec addressing modes).
+;;
+(define_expand "decrement_and_branch_until_zero"
+ [(parallel [(set (pc)
+ (if_then_else
+ (ne (plus:HI (match_operand:HI 0 "register_operand" "")
+ (const_int 0))
+ (const_int 1))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (const_int -1)))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_M6812"
+ "")
+
+(define_expand "doloop_end"
+ [(use (match_operand 0 "" "")) ; loop pseudo
+ (use (match_operand 1 "" "")) ; iterations; zero if unknown
+ (use (match_operand 2 "" "")) ; max iterations
+ (use (match_operand 3 "" "")) ; loop level
+ (use (match_operand 4 "" ""))] ; label
+ "TARGET_M6812"
+ "
+{
+ /* Reject non-constant loops as it generates bigger code due to
+ the handling of the loop register. We can do better by using
+ the peephole2 dbcc/ibcc patterns. */
+ if (INTVAL (operands[1]) == 0)
+ {
+ FAIL;
+ }
+
+ /* Note that for xxx_dbcc_dec_yy the gen_rtx_NE is only used to pass
+ the operator and its operands are not relevant. */
+ if (GET_MODE (operands[0]) == HImode)
+ {
+ emit_jump_insn (gen_m68hc12_dbcc_dec_hi (operands[0],
+ gen_rtx_NE (HImode,
+ operands[0],
+ const1_rtx),
+ operands[4]));
+ DONE;
+ }
+ if (GET_MODE (operands[0]) == QImode)
+ {
+ emit_jump_insn (gen_m68hc12_dbcc_dec_qi (operands[0],
+ gen_rtx_NE (QImode,
+ operands[0],
+ const1_rtx),
+ operands[4]));
+ DONE;
+ }
+
+ FAIL;
+}")
+
+;; Decrement-and-branch insns.
+(define_insn "m68hc12_dbcc_dec_hi"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "m68hc11_eq_compare_operator"
+ [(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
+ (const_int 1)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0) (const_int -1)))
+ (clobber (match_scratch:HI 3 "=X,dxy"))]
+ "TARGET_M6812"
+ "*
+{
+ if (!H_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ if (GET_CODE (operands[1]) == EQ)
+ return \"dbeq\\t%0,%l2\";
+ else
+ return \"dbne\\t%0,%l2\";
+}")
+
+;; Decrement-and-branch insns.
+(define_insn "m68hc12_dbcc_inc_hi"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "m68hc11_eq_compare_operator"
+ [(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
+ (const_int -1)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0) (const_int 1)))
+ (clobber (match_scratch:HI 3 "=X,dxy"))]
+ "TARGET_M6812"
+ "*
+{
+ if (!H_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ if (GET_CODE (operands[1]) == EQ)
+ return \"ibeq\\t%0,%l2\";
+ else
+ return \"ibeq\\t%0,%l2\";
+}")
+
+;; Decrement-and-branch (QImode).
+(define_insn "m68hc12_dbcc_dec_qi"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "m68hc11_eq_compare_operator"
+ [(match_operand:QI 0 "register_operand" "+d,m*u*A")
+ (const_int 1)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0) (const_int -1)))
+ (clobber (match_scratch:QI 3 "=X,d"))]
+ "TARGET_M6812"
+ "*
+{
+ if (!D_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ if (GET_CODE (operands[1]) == EQ)
+ return \"dbeq\\tb,%l2\";
+ else
+ return \"dbne\\tb,%l2\";
+}")
+
+;; Increment-and-branch (QImode).
+(define_insn "m68hc12_dbcc_inc_qi"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "m68hc11_eq_compare_operator"
+ [(match_operand:QI 0 "register_operand" "+d,m*u*A")
+ (const_int -1)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0) (const_int 1)))
+ (clobber (match_scratch:QI 3 "=X,d"))]
+ "TARGET_M6812"
+ "*
+{
+ if (!D_REG_P (operands[0]))
+ return \"#\";
+
+ CC_STATUS_INIT;
+ if (GET_CODE (operands[1]) == EQ)
+ return \"ibeq\\tb,%l2\";
+ else
+ return \"ibeq\\tb,%l2\";
+}")
+
+;; Split the above to handle the case where operand 0 is in memory
+;; (a register that couldn't get a hard register)
+(define_split
+ [(set (pc)
+ (if_then_else
+ (match_operator 3 "m68hc11_eq_compare_operator"
+ [(match_operand:HI 0 "general_operand" "")
+ (match_operand:HI 1 "const_int_operand" "")])
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0) (match_operand 2 "const_int_operand" "")))
+ (clobber (match_operand:HI 5 "hard_reg_operand" ""))]
+ "TARGET_M6812 && reload_completed"
+ [(set (match_dup 5) (match_dup 0))
+ (set (match_dup 5) (plus:HI (match_dup 5) (match_dup 2)))
+ (set (match_dup 0) (match_dup 5))
+ (set (pc)
+ (if_then_else (match_op_dup 3
+ [(match_dup 5) (const_int 0)])
+ (label_ref (match_dup 4)) (pc)))]
+ "")
+
+;; Split the above to handle the case where operand 0 is in memory
+;; (a register that couldn't get a hard register)
+(define_split
+ [(set (pc)
+ (if_then_else
+ (match_operator 3 "m68hc11_eq_compare_operator"
+ [(match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "const_int_operand" "")])
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0) (match_operand 2 "const_int_operand" "")))
+ (clobber (match_operand:QI 5 "hard_reg_operand" ""))]
+ "TARGET_M6812 && reload_completed"
+ [(set (match_dup 5) (match_dup 0))
+ (set (match_dup 5) (plus:QI (match_dup 5) (match_dup 2)))
+ (set (match_dup 0) (match_dup 5))
+ (set (pc)
+ (if_then_else (match_op_dup 3
+ [(match_dup 5) (const_int 0)])
+ (label_ref (match_dup 4)) (pc)))]
+ "")
+
+;;--------------------------------------------------------------------
+;;- Jumps and transfers
+;;--------------------------------------------------------------------
+(define_insn "jump"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ ""
+ "bra\\t%l0")
+
+(define_expand "cbranchsi4"
+ [(set (cc0)
+ (compare (match_operand:SI 1 "tst_operand" "")
+ (match_operand:SI 2 "cmp_operand" "")))
+ (set (pc)
+ (if_then_else (match_operator 0 "ordered_comparison_operator"
+ [(cc0) (const_int 0)])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[2]) == MEM)
+ operands[1] = force_reg (SImode, operands[1]);
+
+ m68hc11_expand_compare_and_branch (GET_CODE (operands[0]), operands[1],
+ operands[2], operands[3]);
+ DONE;
+}")
+
+(define_expand "cbranchhi4"
+ [(set (cc0)
+ (compare (match_operand:HI 1 "tst_operand" "")
+ (match_operand:HI 2 "cmp_operand" "")))
+ (set (pc)
+ (if_then_else (match_operator 0 "ordered_comparison_operator"
+ [(cc0) (const_int 0)])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[2]) == MEM)
+ operands[1] = force_reg (HImode, operands[1]);
+
+ m68hc11_expand_compare_and_branch (GET_CODE (operands[0]), operands[1],
+ operands[2], operands[3]);
+ DONE;
+}")
+
+(define_expand "cbranchqi4"
+ [(set (cc0)
+ (compare (match_operand:QI 1 "tst_operand" "")
+ (match_operand:QI 2 "cmp_operand" "")))
+ (set (pc)
+ (if_then_else (match_operator 0 "ordered_comparison_operator"
+ [(cc0) (const_int 0)])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[2]) == MEM)
+ operands[1] = force_reg (QImode, operands[1]);
+
+ m68hc11_expand_compare_and_branch (GET_CODE (operands[0]), operands[1],
+ operands[2], operands[3]);
+ DONE;
+}")
+
+
+;;
+;; Test and branch instructions for 68HC12 for EQ and NE.
+;; 'z' must not appear in the constraints because the z replacement
+;; pass does not know how to restore the replacement register.
+;;
+(define_insn "*tbeq"
+ [(set (pc)
+ (if_then_else (eq (match_operand:HI 0 "register_operand" "dxy")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ "TARGET_M6812"
+ "*
+{
+ /* If the flags are already set correctly, use 'bne/beq' which are
+ smaller and a little bit faster. This happens quite often due
+ to reloading of operands[0]. In that case, flags are set correctly
+ due to the load instruction. */
+ if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
+ || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
+ return \"beq\\t%l1\";
+ else
+ return \"tbeq\\t%0,%l1\";
+}")
+
+(define_insn "*tbne"
+ [(set (pc)
+ (if_then_else (ne (match_operand:HI 0 "register_operand" "dxy")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ "TARGET_M6812"
+ "*
+{
+ if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
+ || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
+ return \"bne\\t%l1\";
+ else
+ return \"tbne\\t%0,%l1\";
+}")
+
+;;
+;; Test and branch with 8-bit register. Register must be B (or A).
+;;
+(define_insn "*tbeq8"
+ [(set (pc)
+ (if_then_else (eq (match_operand:QI 0 "register_operand" "d")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ "TARGET_M6812"
+ "*
+{
+ if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
+ || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
+ return \"beq\\t%l1\";
+ else
+ return \"tbeq\\tb,%l1\";
+}")
+
+(define_insn "*tbne8"
+ [(set (pc)
+ (if_then_else (ne (match_operand:QI 0 "register_operand" "d")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ "TARGET_M6812"
+ "*
+{
+ if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
+ || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
+ return \"bne\\t%l1\";
+ else
+ return \"tbne\\tb,%l1\";
+}")
+
+(define_insn "*beq"
+ [(set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "beq\\t%l0")
+
+(define_insn "*bne"
+ [(set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "bne\\t%l0")
+
+(define_insn "*bgt"
+ [(set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "bgt\\t%l0")
+
+(define_insn "*bgtu"
+ [(set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "bhi\\t%l0")
+
+(define_insn "*blt"
+ [(set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bmi\\t%l0\";
+ else
+ return \"blt\\t%l0\";
+}")
+
+(define_insn "*bltu"
+ [(set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "blo\\t%l0")
+
+(define_insn "*bge"
+ [(set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bpl\\t%l0\";
+ else
+ return \"bge\\t%l0\";
+}")
+
+(define_insn "*bgeu"
+ [(set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "bhs\\t%l0")
+
+(define_insn "*ble"
+ [(set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
+ else
+ return \"ble\\t%l0\";
+}")
+
+(define_insn "*bleu"
+ [(set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "bls\\t%l0")
+
+;;--------------------------------------------------------------------
+;;- Negative test and branch
+;;--------------------------------------------------------------------
+(define_insn ""
+ [(set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "bne\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "beq\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
+ else
+ return \"ble\\t%l0\";
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "bls\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bpl\\t%l0\";
+ else
+ return \"bge\\t%l0\";
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "bhs\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
+ return \"bmi\\t%l0\";
+ else
+ return \"blt\\t%l0\";
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "blo\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "bgt\\t%l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "bhi\\t%l0")
+
+;;--------------------------------------------------------------------
+;;- Calls
+;;--------------------------------------------------------------------
+;;
+;;- Call a function that returns no value.
+(define_insn "call"
+ [(call (match_operand:QI 0 "memory_operand" "m")
+ (match_operand:SI 1 "general_operand" "g"))]
+ ;; Operand 1 not really used on the m68hc11.
+ ""
+ "*
+{
+ if (GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
+ {
+ if (m68hc11_is_far_symbol (operands[0]))
+ {
+ if (TARGET_M6812)
+ {
+ output_asm_insn (\"call\\t%0\", operands);
+ return \"\";
+ }
+ else
+ {
+ output_asm_insn (\"pshb\", operands);
+ output_asm_insn (\"ldab\\t#%%page(%0)\", operands);
+ output_asm_insn (\"ldy\\t#%%addr(%0)\", operands);
+ return \"jsr\\t__call_a32\";
+ }
+ }
+ if (m68hc11_is_trap_symbol (operands[0]))
+ return \"swi\";
+ else
+ return \"bsr\\t%0\";
+ }
+ else
+ {
+ return \"jsr\\t%0\";
+ }
+}")
+
+(define_insn "call_value"
+ [(set (match_operand 0 "" "=g")
+ (call (match_operand:QI 1 "memory_operand" "m")
+ (match_operand:SI 2 "general_operand" "g")))]
+ ""
+ "*
+{
+ if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
+ {
+ if (m68hc11_is_far_symbol (operands[1]))
+ {
+ if (TARGET_M6812)
+ {
+ output_asm_insn (\"call\\t%1\", operands);
+ return \"\";
+ }
+ else
+ {
+ output_asm_insn (\"pshb\", operands);
+ output_asm_insn (\"ldab\\t#%%page(%1)\", operands);
+ output_asm_insn (\"ldy\\t#%%addr(%1)\", operands);
+ return \"jsr\\t__call_a32\";
+ }
+ }
+ if (m68hc11_is_trap_symbol (operands[1]))
+ return \"swi\";
+ else
+ return \"bsr\\t%1\";
+ }
+ else
+ {
+ return \"jsr\\t%1\";
+ }
+}")
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "" "")
+ (const_int 0))
+ (match_operand 1 "" "")
+ (match_operand 2 "" "")])]
+ ""
+ "
+{
+ int i;
+
+ emit_call_insn (gen_call (operands[0], const0_rtx));
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+ rtx set = XVECEXP (operands[2], 0, i);
+ emit_move_insn (SET_DEST (set), SET_SRC (set));
+ }
+
+ /* The optimizer does not know that the call sets the function value
+ registers we stored in the result block. We avoid problems by
+ claiming that all hard registers are used and clobbered at this
+ point. */
+ emit_insn (gen_blockage ());
+
+ DONE;
+}")
+
+;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
+;; all of memory. This blocks insns from being moved across this point.
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] 0)]
+ ""
+ "")
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop")
+
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+ "
+{
+ expand_prologue ();
+ DONE;
+}")
+
+(define_expand "epilogue"
+ [(return)]
+ ""
+ "
+{
+ expand_epilogue ();
+ DONE;
+}")
+
+;; Used for frameless functions which save no regs and allocate no locals.
+(define_expand "return"
+ [(return)]
+ "reload_completed && m68hc11_total_frame_size () == 0"
+ "
+{
+ int ret_size = 0;
+
+ if (crtl->return_rtx)
+ ret_size = GET_MODE_SIZE (GET_MODE (crtl->return_rtx));
+
+ /* Emit use notes only when HAVE_return is true. */
+ if (m68hc11_total_frame_size () != 0)
+ ret_size = 0;
+
+ if (ret_size && ret_size <= 2)
+ {
+ emit_jump_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_RETURN (VOIDmode),
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_REG (HImode, 1)))));
+ DONE;
+ }
+ if (ret_size)
+ {
+ emit_jump_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_RETURN (VOIDmode),
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_REG (SImode, 0)))));
+ DONE;
+ }
+}")
+
+(define_insn "*return_void"
+ [(return)]
+ "reload_completed"
+ "*
+{
+ rtx next = next_active_insn (insn);
+
+ if (next
+ && GET_CODE (next) == JUMP_INSN
+ && GET_CODE (PATTERN (next)) == RETURN)
+ return \"\";
+ if (current_function_interrupt || current_function_trap)
+ return \"rti\";
+ else if (!current_function_far)
+ return \"rts\";
+ else if (TARGET_M6812)
+ return \"rtc\";
+ else
+ {
+ int ret_size = 0;
+
+ if (crtl->return_rtx)
+ ret_size = GET_MODE_SIZE (GET_MODE (crtl->return_rtx));
+
+ if (ret_size == 0)
+ return \"jmp\\t__return_void\";
+ if (ret_size <= 2)
+ return \"jmp\\t__return_16\";
+ if (ret_size <= 4)
+ return \"jmp\\t__return_32\";
+ return \"jmp\\t__return_16\";
+ }
+}")
+
+(define_insn "*return_16bit"
+ [(return)
+ (use (reg:HI D_REGNUM))]
+ "reload_completed && m68hc11_total_frame_size () == 0"
+ "*
+{
+ rtx next = next_active_insn (insn);
+
+ if (next
+ && GET_CODE (next) == JUMP_INSN
+ && GET_CODE (PATTERN (next)) == RETURN)
+ return \"\";
+ if (current_function_interrupt || current_function_trap)
+ return \"rti\";
+ else if (!current_function_far)
+ return \"rts\";
+ else if (TARGET_M6812)
+ return \"rtc\";
+ else
+ return \"jmp\\t__return_16\";
+}")
+
+(define_insn "*return_32bit"
+ [(return)
+ (use (reg:SI 0))]
+ "reload_completed && m68hc11_total_frame_size () == 0"
+ "*
+{
+ rtx next = next_active_insn (insn);
+
+ if (next
+ && GET_CODE (next) == JUMP_INSN
+ && GET_CODE (PATTERN (next)) == RETURN)
+ return \"\";
+ if (current_function_interrupt || current_function_trap)
+ return \"rti\";
+ else if (!current_function_far)
+ return \"rts\";
+ else if (TARGET_M6812)
+ return \"rtc\";
+ else
+ return \"jmp\\t__return_32\";
+}")
+
+(define_insn "indirect_jump"
+ [(set (pc) (match_operand:HI 0 "nonimmediate_operand" "xy"))]
+ ""
+ "jmp\\t0,%0")
+
+;;--------------------------------------------------------------------
+;;- Table jump
+;;--------------------------------------------------------------------
+;;
+;; Operand 0 is the address of the table element to use
+;; operand 1 is the CODE_LABEL for the table
+;;--------------------------------------------------------------------
+(define_expand "tablejump"
+ [(parallel [(set (pc) (match_operand 0 "" ""))
+ (use (label_ref (match_operand 1 "" "")))])]
+ ""
+ "")
+
+(define_insn "*jump_indirect"
+ [(parallel [
+ (set (pc) (match_operand:HI 0 "register_operand" "xy"))
+ (use (label_ref (match_operand 1 "" "")))])]
+ ""
+ "jmp\\t0,%0")
+
+;;--------------------------------------------------------------------
+;;- Peepholes
+;;--------------------------------------------------------------------
+
+;;--------------------------------------------------------------------
+;;- 68HC12 dbcc/ibcc peepholes
+;;--------------------------------------------------------------------
+;;
+;; Replace: "addd #-1; bne L1" into "dbne d,L1"
+;; "addd #-1; beq L1" into "dbeq d,L1"
+;; "addd #1; bne L1" into "ibne d,L1"
+;; "addd #1; beq L1" into "ibeq d,L1"
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (plus:HI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (set (pc)
+ (if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
+ [(match_dup 0)
+ (const_int 0)])
+ (label_ref (match_operand 3 "" "")) (pc)))]
+ "TARGET_M6812 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
+ [(parallel [
+ (set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
+ (label_ref (match_dup 3)) (pc)))
+ (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))
+ (clobber (match_dup 4))])]
+ "operands[4] = gen_rtx_SCRATCH(HImode);
+ operands[5] = GEN_INT (-INTVAL (operands[1]));")
+
+
+;;
+;; Replace: "addb #-1; bne L1" into "dbne b,L1"
+;; "addb #-1; beq L1" into "dbeq b,L1"
+;;
+(define_peephole2
+ [(set (match_operand:QI 0 "hard_reg_operand" "")
+ (plus:QI (match_dup 0)
+ (match_operand:QI 1 "const_int_operand" "")))
+ (set (pc)
+ (if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
+ [(match_dup 0)
+ (const_int 0)])
+ (label_ref (match_operand 3 "" "")) (pc)))]
+ "TARGET_M6812 && D_REG_P (operands[0])
+ && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
+ [(parallel [
+ (set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
+ (label_ref (match_dup 3)) (pc)))
+ (set (match_dup 0) (plus:QI (match_dup 0) (match_dup 1)))
+ (clobber (match_dup 4))])]
+ "operands[4] = gen_rtx_SCRATCH(QImode);
+ operands[5] = GEN_INT (-INTVAL (operands[1]));")
+
+
+;;--------------------------------------------------------------------
+;;- Move peephole2
+;;--------------------------------------------------------------------
+
+;;
+;; Replace "leas 2,sp" with a "pulx" or a "puly".
+;; On 68HC12, this is one cycle slower but one byte smaller.
+;; pr target/6899: This peephole was not valid because a register CSE
+;; pass removes the pulx/puly. The 'use' clause ensure that the pulx is
+;; not removed.
+;;
+(define_peephole2
+ [(set (reg:HI SP_REGNUM) (plus:HI (reg:HI SP_REGNUM) (const_int 2)))
+ (match_scratch:HI 0 "xy")]
+ "TARGET_M6812 && optimize_size"
+ [(set (match_dup 0) (match_dup 1))
+ (use (match_dup 0))]
+ "operands[1] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));")
+
+;; Replace: "pshx; tfr d,x; stx 0,sp" into "pshd; tfr d,x"
+;;
+;; PR 14542: emit a use to pretend we need the value of initial register.
+;; Otherwise verify_local_live_at_start will die due to a live change
+;; of that register.
+;;
+(define_peephole2
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (match_operand:HI 0 "hard_reg_operand" ""))
+ (set (match_dup 0)
+ (match_operand:HI 1 "hard_reg_operand" ""))
+ (set (mem:HI (reg:HI SP_REGNUM))
+ (match_dup 0))]
+ "TARGET_M6812"
+ [(use (match_dup 0))
+ (set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (match_dup 1))
+ (set (match_dup 0) (match_dup 1))]
+ "")
+
+;;
+;; Change: "ldd 0,sp; pulx" into "puld"
+;; This sequence usually appears at end a functions.
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (mem:HI (reg:HI SP_REGNUM)))
+ (use (match_dup 0))
+ (set (match_operand:HI 1 "hard_reg_operand" "")
+ (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "peep2_reg_dead_p (2, operands[1])"
+ [(set (match_dup 0) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))
+ (use (match_dup 0))]
+ "")
+
+;; Replace: "pshx; clr 0,sp; clr 1,sp" by "clr 1,-sp; clr 1,-sp"
+;; Appears to allocate local variables.
+(define_peephole2
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (match_operand:HI 0 "hard_reg_operand" ""))
+ (set (mem:QI (plus:HI (reg:HI SP_REGNUM) (const_int 1)))
+ (const_int 0))
+ (set (mem:QI (reg:HI SP_REGNUM))
+ (const_int 0))]
+ "TARGET_M6812"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (const_int 0))]
+ "")
+
+;; Likewise for HI mode
+(define_peephole2
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (match_operand:HI 0 "hard_reg_operand" ""))
+ (set (mem:HI (reg:HI SP_REGNUM))
+ (const_int 0))]
+ "TARGET_M6812"
+ [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (const_int 0))]
+ "")
+;;--------------------------------------------------------------------
+;;-
+;;--------------------------------------------------------------------
+;;
+;; Optimize memory<->memory moves when the value is also loaded in
+;; a register.
+;;
+(define_peephole2
+ [(set (match_operand:QI 0 "memory_operand" "")
+ (match_operand:QI 1 "memory_operand" ""))
+ (set (reg:QI D_REGNUM)
+ (match_operand:QI 2 "memory_operand" ""))]
+ "(rtx_equal_p (operands[0], operands[2]) && !side_effects_p (operands[0]))
+ || (GET_CODE (XEXP (operands[0], 0)) == REG
+ && GET_CODE (XEXP (operands[2], 0)) == POST_INC
+ && rtx_equal_p (XEXP (operands[0], 0), XEXP (XEXP (operands[2], 0), 0)))"
+ [(set (reg:QI D_REGNUM) (match_dup 1))
+ (set (match_dup 2) (reg:QI D_REGNUM))]
+ "")
+
+;;
+;; Remove a possible move before a compare instruction when that
+;; move will go in a dead register. Compare with the source then.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "hard_reg_operand" ""))
+ (set (cc0)
+ (compare (match_dup 0)
+ (match_operand:HI 2 "cmp_operand" "")))]
+ "(X_REG_P (operands[1]) || Y_REG_P (operands[1]))
+ && peep2_reg_dead_p (2, operands[0])
+ && !reg_mentioned_p (operands[0], operands[2])"
+ [(set (cc0) (compare (match_dup 1) (match_dup 2)))]
+ "")
+
+;;
+;; Optimize loading a constant to memory when that same constant
+;; is loaded to a hard register. Switch the two to use the register
+;; for memory initialization. In most cases, the constant is 0.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "memory_operand" "")
+ (match_operand:HI 1 "immediate_operand" ""))
+ (set (match_operand:HI 2 "hard_reg_operand" "")
+ (match_dup 1))]
+ "(D_REG_P (operands[2]) || X_REG_P (operands[2]) || Y_REG_P (operands[2]))
+ && !reg_mentioned_p (operands[2], operands[0])"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+;;
+;; Reorganize to optimize address computations.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))]
+ "(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
+ "")
+
+;;
+;; Replace: "ldx #N; xgdx; addd <var>; xgdx" by "ldab #N; ldx <var>; abx"
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))
+ (match_scratch:QI 3 "d")]
+ "TARGET_M6811 && (INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 0x0ff)"
+ [(set (match_dup 3) (match_dup 4))
+ (set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (plus:HI (zero_extend:HI (match_dup 3)) (match_dup 0)))]
+ "operands[4] = m68hc11_gen_lowpart (QImode, operands[1]);")
+
+;;
+;; Replace: "ldx #N; xgdx; addd <var>; xgdx" by "ldab #N; ldx <var>; abx"
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))]
+ "TARGET_M6812"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
+ "")
+
+;;
+;; Optimize an address register increment and a compare to use
+;; a PRE_INC or PRE_DEC addressing mode (disabled on the tst insn
+;; before reload, but can be enabled after).
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (plus:HI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (set (cc0)
+ (compare (match_operand:QI 2 "memory_operand" "")
+ (const_int 0)))]
+ "TARGET_AUTO_INC_DEC
+ && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
+ && reg_mentioned_p (operands[0], operands[2])"
+ [(set (cc0)
+ (compare (match_dup 3)
+ (const_int 0)))]
+ "if (INTVAL (operands[1]) == 1)
+ operands[3] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_INC (HImode, operands[0]));
+ else
+ operands[3] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_DEC (HImode, operands[0]));
+ ")
+
+;;
+;; Likewise for compare.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (plus:HI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (set (cc0)
+ (compare (match_operand:QI 2 "hard_reg_operand" "")
+ (match_operand:QI 3 "memory_operand" "")))]
+ "TARGET_AUTO_INC_DEC
+ && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
+ && reg_mentioned_p (operands[0], operands[3])"
+ [(set (cc0) (compare (match_dup 2) (match_dup 4)))]
+ "if (INTVAL (operands[1]) == 1)
+ operands[4] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_INC (HImode, operands[0]));
+ else
+ operands[4] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_DEC (HImode, operands[0]));
+ ")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (plus:HI (match_dup 0)
+ (match_operand:HI 1 "const_int_operand" "")))
+ (set (cc0)
+ (compare (match_operand:QI 2 "memory_operand" "")
+ (match_operand:QI 3 "hard_reg_operand" "")))]
+ "TARGET_AUTO_INC_DEC
+ && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
+ && reg_mentioned_p (operands[0], operands[2])"
+ [(set (cc0) (compare (match_dup 4) (match_dup 3)))]
+ "if (INTVAL (operands[1]) == 1)
+ operands[4] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_INC (HImode, operands[0]));
+ else
+ operands[4] = gen_rtx_MEM (QImode,
+ gen_rtx_PRE_DEC (HImode, operands[0]));
+ ")
+
+;;
+;; Replace a "ldx #N; addx <sp>" with a "ldx <sp>; addx #n"
+;; (avoids many temporary moves because we can't add sp to another reg easily)
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
+ ""
+ [(set (match_dup 0) (reg:HI SP_REGNUM))
+ (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
+ "")
+
+;;
+;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (match_operand:HI 2 "general_operand" "")))]
+ "(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
+ "")
+
+;;
+;;
+;;
+(define_peephole2
+ [(parallel
+ [(set (match_operand:SI 0 "hard_reg_operand" "")
+ (ashift:SI (match_operand:SI 1 "general_operand" "")
+ (const_int 1)))
+ (clobber (match_scratch:HI 2 ""))])
+ (set (match_operand:HI 3 "nonimmediate_operand" "") (reg:HI D_REGNUM))
+ (set (match_operand:HI 4 "nonimmediate_operand" "") (reg:HI X_REGNUM))]
+ "!X_REG_P (operands[1])
+ && peep2_reg_dead_p (2, gen_rtx_REG (HImode, D_REGNUM))
+ && peep2_reg_dead_p (3, gen_rtx_REG (HImode, X_REGNUM))"
+ [(set (reg:HI D_REGNUM) (match_dup 5))
+ (set (reg:HI D_REGNUM) (ashift:HI (reg:HI D_REGNUM) (const_int 1)))
+ (set (match_dup 3) (reg:HI D_REGNUM))
+ (set (reg:HI D_REGNUM) (match_dup 6))
+ (parallel [(set (reg:HI D_REGNUM)
+ (rotate:HI (reg:HI D_REGNUM) (const_int 1)))
+ (clobber (reg:HI CC_REGNUM))])
+ (set (match_dup 4) (reg:HI D_REGNUM))]
+ "operands[5] = m68hc11_gen_lowpart (HImode, operands[1]);
+ operands[6] = m68hc11_gen_highpart (HImode, operands[1]);")
+
+;;
+;; Replace a "ldd <mem>; psha; pshb" with a "ldx <mem>; pshx".
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "memory_operand" ""))
+ (set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
+ (match_dup 0))
+ (match_scratch:HI 2 "x")]
+ "TARGET_M6811 && D_REG_P (operands[0]) && peep2_reg_dead_p (2, operands[0])"
+ [(set (match_dup 2) (match_dup 1))
+ (set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))]
+ "")
+
+;;
+;; Remove one load when copying a value to/from memory and also
+;; to a register. Take care not clobbering a possible register used
+;; by operand 2.
+;; Replace: "ldd 0,y; std 2,y; ldx 0,y" into "ldx 0,y; stx 2,y"
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "general_operand" ""))
+ (set (match_operand:HI 2 "nonimmediate_operand" "") (match_dup 0))
+ (set (match_operand:HI 3 "hard_reg_operand" "") (match_dup 1))]
+ "peep2_reg_dead_p (2, operands[0])
+ && !side_effects_p (operands[1])
+ && !side_effects_p (operands[2])
+ && !reg_mentioned_p (operands[3], operands[2])"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 2) (match_dup 3))]
+ "")
+
+;;
+;; Replace a "ldd <mem>; addd #N; std <mem>" into a
+;; "ldx <mem>; leax; stx <mem>" if we have a free X/Y register
+;; and the constant is small.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "general_operand" ""))
+ (set (match_dup 0) (plus:HI (match_dup 0)
+ (match_operand:HI 2 "const_int_operand" "")))
+ (set (match_operand:HI 3 "nonimmediate_operand" "")
+ (match_dup 0))
+ (match_scratch:HI 4 "xy")]
+ "D_REG_P (operands[0])
+ && (TARGET_M6812
+ || (INTVAL (operands[2]) >= -2 && INTVAL (operands[2]) <= 2))
+ && peep2_reg_dead_p (3, operands[0])"
+ [(set (match_dup 4) (match_dup 1))
+ (set (match_dup 4) (plus:HI (match_dup 4) (match_dup 2)))
+ (set (match_dup 3) (match_dup 4))]
+ "if (reg_mentioned_p (operands[4], operands[1])) FAIL;
+ if (reg_mentioned_p (operands[4], operands[3])) FAIL;")
+
+;;--------------------------------------------------------------------
+;;- Bset peephole2
+;;--------------------------------------------------------------------
+;; These peepholes try to replace some logical sequences by 'bset' and 'bclr'.
+;;
+;; Replace 'ldab <mem>; orab #N; stab <mem>' by 'bset <mem> #N'.
+;; Register D must be dead and there must be no register side effects for mem.
+;; The <mem> *can* be volatile this is why we must not use 'side_effects_p'.
+;; The good side effect is that it makes the sequence atomic.
+;;
+(define_peephole2
+ [(set (match_operand:QI 0 "hard_reg_operand" "")
+ (match_operand:QI 1 "nonimmediate_operand" ""))
+ (set (match_dup 0) (ior:QI (match_dup 0)
+ (match_operand:QI 2 "const_int_operand" "")))
+ (set (match_dup 1) (match_dup 0))]
+ "(TARGET_M6812 || m68hc11_indirect_p (operands[1], QImode))
+ && (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
+ && peep2_reg_dead_p (3, operands[0])"
+ [(set (match_dup 1) (ior:QI (match_dup 1) (match_dup 2)))]
+ "")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "nonimmediate_operand" ""))
+ (set (match_dup 0) (ior:HI (match_dup 0)
+ (match_operand:HI 2 "const_int_operand" "")))
+ (set (match_dup 1) (match_dup 0))]
+ "(TARGET_M6812 || m68hc11_indirect_p (operands[1], HImode))
+ && (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
+ && peep2_reg_dead_p (3, operands[0])"
+ [(set (match_dup 1) (ior:HI (match_dup 1) (match_dup 2)))]
+ "")
+
+;;--------------------------------------------------------------------
+;;- Bclr peephole2
+;;--------------------------------------------------------------------
+;; Replace 'ldab <mem>; andab #N; stab <mem>' by 'bclr <mem> #N'.
+;; See Bset peephole2.
+;;
+(define_peephole2
+ [(set (match_operand:QI 0 "hard_reg_operand" "")
+ (match_operand:QI 1 "nonimmediate_operand" ""))
+ (set (match_dup 0) (and:QI (match_dup 0)
+ (match_operand:QI 2 "const_int_operand" "")))
+ (set (match_dup 1) (match_dup 0))]
+ "(TARGET_M6812 || m68hc11_indirect_p (operands[1], QImode))
+ && (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
+ && peep2_reg_dead_p (3, operands[0])"
+ [(set (match_dup 1) (and:QI (match_dup 1) (match_dup 2)))]
+ "")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "nonimmediate_operand" ""))
+ (set (match_dup 0) (and:HI (match_dup 0)
+ (match_operand:HI 2 "const_int_operand" "")))
+ (set (match_dup 1) (match_dup 0))]
+ "(TARGET_M6812 || m68hc11_indirect_p (operands[1], HImode))
+ && (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
+ && peep2_reg_dead_p (3, operands[0])"
+ [(set (match_dup 1) (and:HI (match_dup 1) (match_dup 2)))]
+ "")
+
+
+;;--------------------------------------------------------------------
+;;- Compare peephole2
+;;--------------------------------------------------------------------
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "hard_reg_operand" ""))
+ (set (match_dup 1) (plus:HI (match_dup 1)
+ (match_operand:HI 2 "const_int_operand" "")))
+ (set (cc0) (compare (match_dup 0)
+ (const_int 0)))]
+ "peep2_reg_dead_p (3, operands[0]) && !Z_REG_P (operands[1])"
+ [(set (match_dup 1) (plus:HI (match_dup 1) (match_dup 2)))
+ (set (cc0) (compare (match_dup 1) (match_dup 2)))]
+ "")
+
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "hard_reg_operand" ""))
+ (set (match_operand:HI 2 "hard_reg_operand" "")
+ (plus:HI (match_dup 2)
+ (match_operand:HI 3 "const_int_operand" "")))
+ (set (match_operand:HI 4 "memory_operand" "") (match_dup 2))
+ (set (cc0) (compare (match_operand:HI 5 "hard_reg_operand" "")
+ (const_int 0)))]
+ "peep2_reg_dead_p (4, operands[5]) && !Z_REG_P (operands[2])
+ && !reg_mentioned_p (operands[2], operands[4])
+
+ && ((rtx_equal_p (operands[5], operands[0])
+ && rtx_equal_p (operands[2], operands[1]))
+
+ || (rtx_equal_p (operands[5], operands[1])
+ && rtx_equal_p (operands[2], operands[0])))"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 2) (plus:HI (match_dup 2) (match_dup 3)))
+ (set (match_dup 4) (match_dup 2))
+ (set (cc0) (compare (match_dup 2) (match_dup 3)))]
+ "")
+
+
+;;--------------------------------------------------------------------
+;;- Load peephole2
+;;--------------------------------------------------------------------
+;;
+;; Optimize initialization of 2 hard regs from the same memory location
+;; Since we can't copy easily X, Y and D to each other, load the 2 registers
+;; from the same memory location.
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "memory_operand" ""))
+ (set (match_operand:HI 2 "hard_reg_operand" "") (match_dup 0))]
+ "TARGET_M6811
+ && !side_effects_p (operands[1])
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 2) (match_dup 1))]
+ "")
+
+;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "nonimmediate_operand" "") (const_int 0))
+ (set (match_operand:HI 1 "nonimmediate_operand" "") (const_int 0))
+ (set (match_operand:HI 2 "nonimmediate_operand" "") (const_int 0))
+ (set (match_operand:HI 3 "nonimmediate_operand" "") (const_int 0))
+ (match_scratch:HI 4 "d")]
+ ""
+ [(set (match_dup 4) (const_int 0))
+ (set (match_dup 0) (match_dup 4))
+ (set (match_dup 1) (match_dup 4))
+ (set (match_dup 2) (match_dup 4))
+ (set (match_dup 3) (match_dup 4))]
+ "")
+
+;;
+;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "nonimmediate_operand" "") (const_int 0))
+ (set (match_operand:HI 1 "nonimmediate_operand" "") (const_int 0))
+ (set (match_operand:HI 2 "nonimmediate_operand" "") (const_int 0))
+ (match_scratch:HI 3 "d")]
+ ""
+ [(set (match_dup 3) (const_int 0))
+ (set (match_dup 0) (match_dup 3))
+ (set (match_dup 1) (match_dup 3))
+ (set (match_dup 2) (match_dup 3))]
+ "")
+
+;;
+;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
+;;
+(define_peephole2
+ [(set (match_operand:HI 0 "hard_reg_operand" "") (const_int 0))
+ (set (match_operand:HI 1 "push_operand" "") (match_dup 0))
+ (set (match_operand:HI 2 "push_operand" "") (match_dup 0))
+ (set (match_operand:HI 3 "push_operand" "") (match_dup 0))
+ (match_scratch:HI 4 "x")]
+ "TARGET_M6811 && D_REG_P (operands[0]) && peep2_reg_dead_p (4, operands[0])"
+ [(set (match_dup 4) (const_int 0))
+ (set (match_dup 1) (match_dup 4))
+ (set (match_dup 2) (match_dup 4))
+ (set (match_dup 3) (match_dup 4))]
+ "")
+
+;;
+;; This peephole catches the address computations generated by the reload
+;; pass.
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "xy")
+ (match_operand:HI 1 "const_int_operand" ""))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM)
+ (plus (reg:HI D_REGNUM)
+ (match_operand:HI 2 "general_operand" "")))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "(INTVAL (operands[1]) & 0x0FF) == 0"
+ "*
+{
+ int value_loaded = 1;
+
+ if (X_REG_P (operands[0]) || SP_REG_P (operands[2]))
+ {
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = operands[2];
+ m68hc11_gen_movhi (insn, ops);
+ output_asm_insn (\"xgd%0\", operands);
+ }
+ else if (Y_REG_P (operands[0]))
+ {
+ if (reg_mentioned_p (iy_reg, operands[2]))
+ output_asm_insn (\"ldy\\t%2\", operands);
+ else
+ value_loaded = 0;
+ output_asm_insn (\"xgdy\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldd\\t%2\", operands);
+ }
+
+ if (value_loaded == 0)
+ output_asm_insn (\"ldd\\t%2\", operands);
+ if ((INTVAL (operands[1]) & 0x0ff00) == 0x100)
+ output_asm_insn (\"inca\", operands);
+ else if ((INTVAL (operands[1]) & 0x0ff00) == 0xff00)
+ output_asm_insn (\"deca\", operands);
+ else if (INTVAL (operands[1]) != 0)
+ output_asm_insn (\"adda\\t%h1\", operands);
+
+ if (X_REG_P (operands[0]))
+ return \"xgdx\";
+ else if (Y_REG_P (operands[0]))
+ return \"xgdy\";
+ else
+ return \"\";
+}
+")
+
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "h")
+ (match_operand:HI 1 "non_push_operand" "g"))
+ (set (match_operand:HI 2 "hard_reg_operand" "h")
+ (match_dup 0))]
+ "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
+ && !S_REG_P (operands[2])"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[2];
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "h")
+ (match_operand:HI 1 "hard_reg_operand" "h"))
+ (set (match_operand:HI 2 "non_push_operand" "g")
+ (match_dup 0))]
+ "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
+ && !S_REG_P (operands[2])"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[2];
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+;;
+;; Catch a (set X/Y D) followed by a swap. In this form, D is dead after
+;; the set, so we don't need to emit anything. 'ins1' refers to the
+;; (set ...) insn.
+;;
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
+ "*
+{
+ cc_status = cc_prev_status;
+ return \"\";
+}
+")
+
+;; Same as above but due to some split, there may be a noop set
+;; between the two.
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
+ (set (match_dup 0) (match_dup 0))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ "find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
+ "*
+{
+ cc_status = cc_prev_status;
+ return \"\";
+}
+")
+
+;;
+;; Catch a (set X/Y D) followed by an xgdx/xgdy. D is not dead
+;; and we must, at least, setup X/Y with value of D.
+;;
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+;;;
+;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
+;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
+;;;
+(define_peephole
+ [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM) (match_dup 0))]
+ "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
+ "*
+{
+ cc_status = cc_prev_status;
+ return \"\";
+}
+")
+
+;;;
+;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
+;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
+;;;
+(define_peephole
+ [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
+ "REGNO (operands[0]) == REGNO (operands[1])
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
+ "*
+{
+ cc_status = cc_prev_status;
+ return \"\";
+}
+")
+
+;;;
+;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
+;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
+;;;
+(define_peephole
+ [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:HI D_REGNUM) (match_dup 0))]
+ ""
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+;;;
+;;; Same peephole with a QI set. The copy is made as 16-bit to comply
+;;; with the xgdx.
+;;;
+(define_peephole
+ [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
+ "REGNO (operands[0]) == REGNO (operands[1])"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+;;;
+;;; Catch two consecutive xgdx or xgdy, emit nothing.
+;;;
+(define_peephole
+ [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
+ (set (match_dup 0) (reg:HI D_REGNUM))])
+ (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
+ (set (match_dup 0) (reg:HI D_REGNUM))])]
+ ""
+ "*
+{
+ cc_status = cc_prev_status;
+ return \"\";
+}
+")
+
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "stack_register_operand" ""))
+ (set (match_operand:HI 2 "hard_reg_operand" "")
+ (match_operand:HI 3 "memory_operand" "m"))
+ (set (match_dup 0)
+ (match_operand:HI 4 "memory_operand" "m"))]
+ "IS_STACK_POP (operands[4])
+ && (GET_CODE (operands[3]) == MEM &&
+ rtx_equal_p (operands[0], XEXP (operands[3], 0)))"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[2];
+ ops[1] = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode, stack_pointer_rtx));
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}
+")
+
+;;
+;; Catch (d = -1) (d = d + sp) to avoid 2 adjust of SP.
+;;
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "dA") (const_int -1))
+ (set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
+ "TARGET_M6811"
+ "*
+{
+ return \"sts\\t%t0\\n\\tld%0\\t%t0\";
+}
+")
+
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "")
+ (match_operand:HI 1 "memory_operand" ""))
+ (set (match_operand:HI 2 "hard_reg_operand" "") (match_dup 0))]
+ "TARGET_M6811
+ && !side_effects_p (operands[1])
+ && !reg_mentioned_p (operands[0], operands[1])"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ ops[0] = operands[2];
+ m68hc11_gen_movhi (insn, ops);
+ return \"\";
+}")
+
+;; Peephole for Z register replacement.
+;; Avoid to use _.tmp register when comparing D and X if we can compare
+;; with soft register
+(define_peephole
+ [(set (match_operand:HI 0 "hard_reg_operand" "") (reg:HI SOFT_XY_REGNUM))
+ (set (reg:HI SOFT_TMP_REGNUM) (match_dup 0))
+ (set (cc0) (compare (match_operand:HI 2 "hard_reg_operand" "")
+ (reg:HI SOFT_TMP_REGNUM)))]
+ "X_REG_P (operands[0]) || Y_REG_P (operands[0])"
+ "*
+{
+ rtx ops[2];
+
+ ops[0] = operands[0];
+ ops[1] = operands[1];
+ m68hc11_gen_movhi (insn, ops);
+ return \"cp%2\\t%1\";
+}")