diff options
Diffstat (limited to 'gcc/config/m68hc11/m68hc11.md')
-rw-r--r-- | gcc/config/m68hc11/m68hc11.md | 7579 |
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\"; +}") |