aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.4.0/gcc/config/m68hc11
diff options
context:
space:
mode:
authorJing Yu <jingyu@google.com>2009-11-05 15:11:04 -0800
committerJing Yu <jingyu@google.com>2009-11-05 15:11:04 -0800
commitdf62c1c110e8532b995b23540b7e3695729c0779 (patch)
treedbbd4cbdb50ac38011e058a2533ee4c3168b0205 /gcc-4.4.0/gcc/config/m68hc11
parent8d401cf711539af5a2f78d12447341d774892618 (diff)
downloadtoolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.gz
toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.tar.bz2
toolchain_gcc-df62c1c110e8532b995b23540b7e3695729c0779.zip
Check in gcc sources for prebuilt toolchains in Eclair.
Diffstat (limited to 'gcc-4.4.0/gcc/config/m68hc11')
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/larith.asm1333
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11-crt0.S86
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11-protos.h130
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11.c5499
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11.h1538
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11.md7730
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc11.opt94
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/m68hc12.h45
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/predicates.md228
-rw-r--r--gcc-4.4.0/gcc/config/m68hc11/t-m68hc1177
10 files changed, 16760 insertions, 0 deletions
diff --git a/gcc-4.4.0/gcc/config/m68hc11/larith.asm b/gcc-4.4.0/gcc/config/m68hc11/larith.asm
new file mode 100644
index 000000000..09f946cbf
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/larith.asm
@@ -0,0 +1,1333 @@
+/* libgcc routines for M68HC11 & M68HC12.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2008, 2009
+ Free Software Foundation, Inc.
+
+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.
+
+This file 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifdef __HAVE_SHORT_INT__
+ .mode mshort
+#else
+ .mode mlong
+#endif
+
+ .macro declare_near name
+ .globl \name
+ .type \name,@function
+ .size \name,.Lend-\name
+\name:
+ .endm
+
+#if defined(__USE_RTC__)
+# define ARG(N) N+1
+
+ .macro ret
+#if defined(mc68hc12)
+ rtc
+#else
+ jmp __return_32
+#endif
+ .endm
+
+ .macro declare name
+ .globl \name
+ .type \name,@function
+ .size \name,.Lend-\name
+ .far \name
+\name:
+ .endm
+
+ .macro farsym name
+ .far NAME
+ .endm
+
+#else
+# define ARG(N) N
+
+ .macro ret
+ rts
+ .endm
+
+ .macro farsym name
+ .endm
+
+ .macro declare name
+ .globl \name
+ .type \name,@function
+ .size \name,.Lend-\name
+\name:
+ .endm
+
+#endif
+
+ .sect .text
+
+
+#define REG(NAME) \
+NAME: .dc.w 1; \
+ .type NAME,@object ; \
+ .size NAME,2
+
+#ifdef L_regs_min
+/* Pseudo hard registers used by gcc.
+ They should be located in page0. */
+
+ .sect .softregs
+ .globl _.tmp
+ .globl _.z,_.xy
+REG(_.tmp)
+REG(_.z)
+REG(_.xy)
+
+#endif
+
+#ifdef L_regs_frame
+ .sect .softregs
+ .globl _.frame
+REG(_.frame)
+#endif
+
+#ifdef L_regs_d1_2
+ .sect .softregs
+ .globl _.d1,_.d2
+REG(_.d1)
+REG(_.d2)
+#endif
+
+#ifdef L_regs_d3_4
+ .sect .softregs
+ .globl _.d3,_.d4
+REG(_.d3)
+REG(_.d4)
+#endif
+
+#ifdef L_regs_d5_6
+ .sect .softregs
+ .globl _.d5,_.d6
+REG(_.d5)
+REG(_.d6)
+#endif
+
+#ifdef L_regs_d7_8
+ .sect .softregs
+ .globl _.d7,_.d8
+REG(_.d7)
+REG(_.d8)
+#endif
+
+#ifdef L_regs_d9_16
+/* Pseudo hard registers used by gcc.
+ They should be located in page0. */
+ .sect .softregs
+ .globl _.d9,_.d10,_.d11,_.d12,_.d13,_.d14
+ .globl _.d15,_.d16
+REG(_.d9)
+REG(_.d10)
+REG(_.d11)
+REG(_.d12)
+REG(_.d13)
+REG(_.d14)
+REG(_.d15)
+REG(_.d16)
+
+#endif
+
+#ifdef L_regs_d17_32
+/* Pseudo hard registers used by gcc.
+ They should be located in page0. */
+ .sect .softregs
+ .globl _.d17,_.d18,_.d19,_.d20,_.d21,_.d22
+ .globl _.d23,_.d24,_.d25,_.d26,_.d27,_.d28
+ .globl _.d29,_.d30,_.d31,_.d32
+REG(_.d17)
+REG(_.d18)
+REG(_.d19)
+REG(_.d20)
+REG(_.d21)
+REG(_.d22)
+REG(_.d23)
+REG(_.d24)
+REG(_.d25)
+REG(_.d26)
+REG(_.d27)
+REG(_.d28)
+REG(_.d29)
+REG(_.d30)
+REG(_.d31)
+REG(_.d32)
+#endif
+
+#ifdef L_premain
+;;
+;; Specific initialization for 68hc11 before the main.
+;; Nothing special for a generic routine; Just enable interrupts.
+;;
+ declare_near __premain
+ clra
+ tap ; Clear both I and X.
+ rts
+#endif
+
+#ifdef L__exit
+;;
+;; Exit operation. Just loop forever and wait for interrupts.
+;; (no other place to go)
+;; This operation is split in several pieces collected together by
+;; the linker script. This allows to support destructors at the
+;; exit stage while not impacting program sizes when there is no
+;; destructors.
+;;
+;; _exit:
+;; *(.fini0) /* Beginning of finish code (_exit symbol). */
+;; *(.fini1) /* Place holder for applications. */
+;; *(.fini2) /* C++ destructors. */
+;; *(.fini3) /* Place holder for applications. */
+;; *(.fini4) /* Runtime exit. */
+;;
+ .sect .fini0,"ax",@progbits
+ .globl _exit
+ .globl exit
+ .weak exit
+ farsym exit
+ farsym _exit
+exit:
+_exit:
+
+ .sect .fini4,"ax",@progbits
+fatal:
+ cli
+ wai
+ bra fatal
+#endif
+
+#ifdef L_abort
+;;
+;; Abort operation. This is defined for the GCC testsuite.
+;;
+ declare abort
+
+ ldd #255 ;
+#ifdef mc68hc12
+ trap #0x30
+#else
+ .byte 0xCD ; Generate an illegal instruction trap
+ .byte 0x03 ; The simulator catches this and stops.
+#endif
+ jmp _exit
+#endif
+
+#ifdef L_cleanup
+;;
+;; Cleanup operation used by exit().
+;;
+ declare _cleanup
+
+ ret
+#endif
+
+;-----------------------------------------
+; required gcclib code
+;-----------------------------------------
+#ifdef L_memcpy
+ declare memcpy
+ declare __memcpy
+
+ .weak memcpy
+;;;
+;;; void* memcpy(void*, const void*, size_t)
+;;;
+;;; D = dst Pmode
+;;; 2,sp = src Pmode
+;;; 4,sp = size HImode (size_t)
+;;;
+#ifdef mc68hc12
+ ldx ARG(2),sp
+ ldy ARG(4),sp
+ pshd
+ xgdy
+ lsrd
+ bcc Start
+ movb 1,x+,1,y+
+Start:
+ beq Done
+Loop:
+ movw 2,x+,2,y+
+ dbne d,Loop
+Done:
+ puld
+ ret
+#else
+ xgdy
+ tsx
+ ldd ARG(4),x
+ ldx ARG(2),x ; SRC = X, DST = Y
+ cpd #0
+ beq End
+ pshy
+ inca ; Correction for the deca below
+L0:
+ psha ; Save high-counter part
+L1:
+ ldaa 0,x ; Copy up to 256 bytes
+ staa 0,y
+ inx
+ iny
+ decb
+ bne L1
+ pula
+ deca
+ bne L0
+ puly ; Restore Y to return the DST
+End:
+ xgdy
+ ret
+#endif
+#endif
+
+#ifdef L_memset
+ declare memset
+ declare __memset
+;;;
+;;; void* memset(void*, int value, size_t)
+;;;
+#ifndef __HAVE_SHORT_INT__
+;;; D = dst Pmode
+;;; 2,sp = src SImode
+;;; 6,sp = size HImode (size_t)
+ val = ARG(5)
+ size = ARG(6)
+#else
+;;; D = dst Pmode
+;;; 2,sp = src SImode
+;;; 6,sp = size HImode (size_t)
+ val = ARG(3)
+ size = ARG(4)
+#endif
+#ifdef mc68hc12
+ xgdx
+ ldab val,sp
+ ldy size,sp
+ pshx
+ beq End
+Loop:
+ stab 1,x+
+ dbne y,Loop
+End:
+ puld
+ ret
+#else
+ xgdx
+ tsy
+ ldab val,y
+ ldy size,y ; DST = X, CNT = Y
+ beq End
+ pshx
+L0:
+ stab 0,x ; Fill up to 256 bytes
+ inx
+ dey
+ bne L0
+ pulx ; Restore X to return the DST
+End:
+ xgdx
+ ret
+#endif
+#endif
+
+#ifdef L_adddi3
+ declare ___adddi3
+
+ tsx
+ xgdy
+ ldd ARG(8),x ; Add LSB
+ addd ARG(16),x
+ std 6,y ; Save (carry preserved)
+
+ ldd ARG(6),x
+ adcb ARG(15),x
+ adca ARG(14),x
+ std 4,y
+
+ ldd ARG(4),x
+ adcb ARG(13),x
+ adca ARG(12),x
+ std 2,y
+
+ ldd ARG(2),x
+ adcb ARG(11),x ; Add MSB
+ adca ARG(10),x
+ std 0,y
+
+ xgdy
+ ret
+#endif
+
+#ifdef L_subdi3
+ declare ___subdi3
+
+ tsx
+ xgdy
+ ldd ARG(8),x ; Subtract LSB
+ subd ARG(16),x
+ std 6,y ; Save, borrow preserved
+
+ ldd ARG(6),x
+ sbcb ARG(15),x
+ sbca ARG(14),x
+ std 4,y
+
+ ldd ARG(4),x
+ sbcb ARG(13),x
+ sbca ARG(12),x
+ std 2,y
+
+ ldd ARG(2),x ; Subtract MSB
+ sbcb ARG(11),x
+ sbca ARG(10),x
+ std 0,y
+
+ xgdy ;
+ ret
+#endif
+
+#ifdef L_notdi2
+ declare ___notdi2
+
+ tsy
+ xgdx
+ ldd ARG(8),y
+ coma
+ comb
+ std 6,x
+
+ ldd ARG(6),y
+ coma
+ comb
+ std 4,x
+
+ ldd ARG(4),y
+ coma
+ comb
+ std 2,x
+
+ ldd ARG(2),y
+ coma
+ comb
+ std 0,x
+ xgdx
+ ret
+#endif
+
+#ifdef L_negsi2
+ declare_near ___negsi2
+
+ comb
+ coma
+ xgdx
+ comb
+ coma
+ inx
+ xgdx
+ bne done
+ inx
+done:
+ rts
+#endif
+
+#ifdef L_one_cmplsi2
+ declare_near ___one_cmplsi2
+
+ comb
+ coma
+ xgdx
+ comb
+ coma
+ xgdx
+ rts
+#endif
+
+#ifdef L_ashlsi3
+ declare_near ___ashlsi3
+
+ xgdy
+ clra
+ andb #0x1f
+ xgdy
+ beq Return
+Loop:
+ lsld
+ xgdx
+ rolb
+ rola
+ xgdx
+ dey
+ bne Loop
+Return:
+ rts
+#endif
+
+#ifdef L_ashrsi3
+ declare_near ___ashrsi3
+
+ xgdy
+ clra
+ andb #0x1f
+ xgdy
+ beq Return
+Loop:
+ xgdx
+ asra
+ rorb
+ xgdx
+ rora
+ rorb
+ dey
+ bne Loop
+Return:
+ rts
+#endif
+
+#ifdef L_lshrsi3
+ declare_near ___lshrsi3
+
+ xgdy
+ clra
+ andb #0x1f
+ xgdy
+ beq Return
+Loop:
+ xgdx
+ lsrd
+ xgdx
+ rora
+ rorb
+ dey
+ bne Loop
+Return:
+ rts
+#endif
+
+#ifdef L_lshrhi3
+ declare_near ___lshrhi3
+
+ cpx #16
+ bge Return_zero
+ cpx #0
+ beq Return
+Loop:
+ lsrd
+ dex
+ bne Loop
+Return:
+ rts
+Return_zero:
+ clra
+ clrb
+ rts
+#endif
+
+#ifdef L_lshlhi3
+ declare_near ___lshlhi3
+
+ cpx #16
+ bge Return_zero
+ cpx #0
+ beq Return
+Loop:
+ lsld
+ dex
+ bne Loop
+Return:
+ rts
+Return_zero:
+ clra
+ clrb
+ rts
+#endif
+
+#ifdef L_rotrhi3
+ declare_near ___rotrhi3
+
+___rotrhi3:
+ xgdx
+ clra
+ andb #0x0f
+ xgdx
+ beq Return
+Loop:
+ tap
+ rorb
+ rora
+ dex
+ bne Loop
+Return:
+ rts
+#endif
+
+#ifdef L_rotlhi3
+ declare_near ___rotlhi3
+
+___rotlhi3:
+ xgdx
+ clra
+ andb #0x0f
+ xgdx
+ beq Return
+Loop:
+ asrb
+ rolb
+ rola
+ rolb
+ dex
+ bne Loop
+Return:
+ rts
+#endif
+
+#ifdef L_ashrhi3
+ declare_near ___ashrhi3
+
+ cpx #16
+ bge Return_minus_1_or_zero
+ cpx #0
+ beq Return
+Loop:
+ asra
+ rorb
+ dex
+ bne Loop
+Return:
+ rts
+Return_minus_1_or_zero:
+ clrb
+ tsta
+ bpl Return_zero
+ comb
+Return_zero:
+ tba
+ rts
+#endif
+
+#ifdef L_ashrqi3
+ declare_near ___ashrqi3
+
+ cmpa #8
+ bge Return_minus_1_or_zero
+ tsta
+ beq Return
+Loop:
+ asrb
+ deca
+ bne Loop
+Return:
+ rts
+Return_minus_1_or_zero:
+ clrb
+ tstb
+ bpl Return_zero
+ coma
+Return_zero:
+ tab
+ rts
+#endif
+
+#ifdef L_lshlqi3
+ declare_near ___lshlqi3
+
+ cmpa #8
+ bge Return_zero
+ tsta
+ beq Return
+Loop:
+ lslb
+ deca
+ bne Loop
+Return:
+ rts
+Return_zero:
+ clrb
+ rts
+#endif
+
+#ifdef L_divmodhi4
+#ifndef mc68hc12
+/* 68HC12 signed divisions are generated inline (idivs). */
+
+ declare_near __divmodhi4
+
+;
+;; D = numerator
+;; X = denominator
+;;
+;; Result: D = D / X
+;; X = D % X
+;;
+ tsta
+ bpl Numerator_pos
+ comb ; D = -D <=> D = (~D) + 1
+ coma
+ xgdx
+ inx
+ tsta
+ bpl Numerator_neg_denominator_pos
+Numerator_neg_denominator_neg:
+ comb ; X = -X
+ coma
+ addd #1
+ xgdx
+ idiv
+ coma
+ comb
+ xgdx ; Remainder <= 0 and result >= 0
+ inx
+ rts
+
+Numerator_pos_denominator_pos:
+ xgdx
+ idiv
+ xgdx ; Both values are >= 0
+ rts
+
+Numerator_pos:
+ xgdx
+ tsta
+ bpl Numerator_pos_denominator_pos
+Numerator_pos_denominator_neg:
+ coma ; X = -X
+ comb
+ xgdx
+ inx
+ idiv
+ xgdx ; Remainder >= 0 but result <= 0
+ coma
+ comb
+ addd #1
+ rts
+
+Numerator_neg_denominator_pos:
+ xgdx
+ idiv
+ coma ; One value is > 0 and the other < 0
+ comb ; Change the sign of result and remainder
+ xgdx
+ inx
+ coma
+ comb
+ addd #1
+ rts
+#endif /* !mc68hc12 */
+#endif
+
+#ifdef L_mulqi3
+ declare_near ___mulqi3
+
+;
+; short __mulqi3(signed char a, signed char b);
+;
+; signed char a -> register A
+; signed char b -> register B
+;
+; returns the signed result of A * B in register D.
+;
+ tsta
+ bmi A_neg
+ tstb
+ bmi B_neg
+ mul
+ rts
+B_neg:
+ negb
+ bra A_or_B_neg
+A_neg:
+ nega
+ tstb
+ bmi AB_neg
+A_or_B_neg:
+ mul
+ coma
+ comb
+ addd #1
+ rts
+AB_neg:
+ negb
+ mul
+ rts
+#endif
+
+#ifdef L_mulhi3
+ declare_near ___mulhi3
+
+;
+;
+; unsigned short ___mulhi3(unsigned short a, unsigned short b)
+;
+; a = register D
+; b = register X
+;
+#ifdef mc68hc12
+ pshx ; Preserve X
+ exg x,y
+ emul
+ exg x,y
+ pulx
+ rts
+#else
+#ifdef NO_TMP
+ ;
+ ; 16-bit multiplication without temp memory location.
+ ; (smaller but slower)
+ ;
+ pshx ; (4)
+ ins ; (3)
+ pshb ; (3)
+ psha ; (3)
+ pshx ; (4)
+ pula ; (4)
+ pulx ; (5)
+ mul ; (10) B.high * A.low
+ xgdx ; (3)
+ mul ; (10) B.low * A.high
+ abx ; (3)
+ pula ; (4)
+ pulb ; (4)
+ mul ; (10) B.low * A.low
+ pshx ; (4)
+ tsx ; (3)
+ adda 1,x ; (4)
+ pulx ; (5)
+ rts ; (5) 20 bytes
+ ; ---
+ ; 91 cycles
+#else
+ stx *_.tmp ; (4)
+ pshb ; (3)
+ ldab *_.tmp+1 ; (3)
+ mul ; (10) A.high * B.low
+ ldaa *_.tmp ; (3)
+ stab *_.tmp ; (3)
+ pulb ; (4)
+ pshb ; (4)
+ mul ; (10) A.low * B.high
+ addb *_.tmp ; (4)
+ stab *_.tmp ; (3)
+ ldaa *_.tmp+1 ; (3)
+ pulb ; (4)
+ mul ; (10) A.low * B.low
+ adda *_.tmp ; (4)
+ rts ; (5) 24/32 bytes
+ ; 77/85 cycles
+#endif
+#endif
+#endif
+
+#ifdef L_mulhi32
+
+;
+;
+; unsigned long __mulhi32(unsigned short a, unsigned short b)
+;
+; a = register D
+; b = value on stack
+;
+; +---------------+
+; | B low | <- 7,x
+; +---------------+
+; | B high | <- 6,x
+; +---------------+
+; | PC low |
+; +---------------+
+; | PC high |
+; +---------------+
+; | Tmp low |
+; +---------------+
+; | Tmp high |
+; +---------------+
+; | A low |
+; +---------------+
+; | A high |
+; +---------------+ <- 0,x
+;
+;
+; <B-low> 5,x
+; <B-high> 4,x
+; <ret> 2,x
+; <A-low> 1,x
+; <A-high> 0,x
+;
+ declare_near __mulhi32
+
+#ifdef mc68hc12
+ ldy 2,sp
+ emul
+ exg x,y
+ rts
+#else
+ pshx ; Room for temp value
+ pshb
+ psha
+ tsx
+ ldab 6,x
+ mul
+ xgdy ; A.high * B.high
+ ldab 7,x
+ pula
+ mul ; A.high * B.low
+ std 2,x
+ ldaa 1,x
+ ldab 6,x
+ mul ; A.low * B.high
+ addd 2,x
+ stab 2,x
+ tab
+ aby
+ bcc N
+ ldab #0xff
+ aby
+ iny
+N:
+ ldab 7,x
+ pula
+ mul ; A.low * B.low
+ adda 2,x
+ pulx ; Drop temp location
+ pshy ; Put high part in X
+ pulx
+ bcc Ret
+ inx
+Ret:
+ rts
+#endif
+#endif
+
+#ifdef L_mulsi3
+
+;
+; <B-low> 8,y
+; <B-high> 6,y
+; <ret> 4,y
+; <tmp> 2,y
+; <A-low> 0,y
+;
+; D,X -> A
+; Stack -> B
+;
+; The result is:
+;
+; (((A.low * B.high) + (A.high * B.low)) << 16) + (A.low * B.low)
+;
+;
+;
+
+ declare __mulsi3
+
+#ifdef mc68hc12
+ pshd ; Save A.low
+ ldy ARG(4),sp
+ emul ; A.low * B.high
+ ldy ARG(6),sp
+ exg x,d
+ emul ; A.high * B.low
+ leax d,x
+ ldy ARG(6),sp
+ puld
+ emul ; A.low * B.low
+ exg d,y
+ leax d,x
+ exg d,y
+ ret
+#else
+B_low = ARG(8)
+B_high = ARG(6)
+A_low = 0
+A_high = 2
+ pshx
+ pshb
+ psha
+ tsy
+;
+; If B.low is 0, optimize into: (A.low * B.high) << 16
+;
+ ldd B_low,y
+ beq B_low_zero
+;
+; If A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
+;
+ cpx #0
+ beq A_high_zero
+ bsr ___mulhi3 ; A.high * B.low
+;
+; If A.low is 0, optimize into: (A.high * B.low) << 16
+;
+ ldx A_low,y
+ beq A_low_zero ; X = 0, D = A.high * B.low
+ std 2,y
+;
+; If B.high is 0, we can avoid the (A.low * B.high) << 16 term.
+;
+ ldd B_high,y
+ beq B_high_zero
+ bsr ___mulhi3 ; A.low * B.high
+ addd 2,y
+ std 2,y
+;
+; Here, we know that A.low and B.low are not 0.
+;
+B_high_zero:
+ ldd B_low,y ; A.low is on the stack
+ bsr __mulhi32 ; A.low * B.low
+ xgdx
+ tsy ; Y was clobbered, get it back
+ addd 2,y
+A_low_zero: ; See A_low_zero_non_optimized below
+ xgdx
+Return:
+ ins
+ ins
+ ins
+ ins
+ ret
+;
+;
+; A_low_zero_non_optimized:
+;
+; At this step, X = 0 and D = (A.high * B.low)
+; Optimize into: (A.high * B.low) << 16
+;
+; xgdx
+; clra ; Since X was 0, clearing D is superfuous.
+; clrb
+; bra Return
+; ----------------
+; B.low == 0, the result is: (A.low * B.high) << 16
+;
+; At this step:
+; D = B.low = 0
+; X = A.high ?
+; A.low is at A_low,y ?
+; B.low is at B_low,y ?
+;
+B_low_zero:
+ ldd A_low,y
+ beq Zero1
+ ldx B_high,y
+ beq Zero2
+ bsr ___mulhi3
+Zero1:
+ xgdx
+Zero2:
+ clra
+ clrb
+ bra Return
+; ----------------
+; A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
+;
+; At this step:
+; D = B.low != 0
+; X = A.high = 0
+; A.low is at A_low,y ?
+; B.low is at B_low,y ?
+;
+A_high_zero:
+ ldd A_low,y ; A.low
+ beq Zero1
+ ldx B_high,y ; B.high
+ beq A_low_B_low
+ bsr ___mulhi3
+ std 2,y
+ bra B_high_zero ; Do the (A.low * B.low) and the add.
+
+; ----------------
+; A.high and B.high are 0 optimize into: (A.low * B.low)
+;
+; At this step:
+; D = B.high = 0
+; X = A.low != 0
+; A.low is at A_low,y != 0
+; B.high is at B_high,y = 0
+;
+A_low_B_low:
+ ldd B_low,y ; A.low is on the stack
+ bsr __mulhi32
+ bra Return
+#endif
+#endif
+
+#ifdef L_map_data
+
+ .sect .install2,"ax",@progbits
+ .globl __map_data_section
+ .globl __data_image
+#ifdef mc68hc12
+ .globl __data_section_size
+#endif
+__map_data_section:
+#ifdef mc68hc12
+ ldx #__data_image
+ ldy #__data_section_start
+ ldd #__data_section_size
+ beq Done
+Loop:
+ movb 1,x+,1,y+
+ dbne d,Loop
+#else
+ ldx #__data_image
+ ldy #__data_section_start
+ bra Start_map
+Loop:
+ ldaa 0,x
+ staa 0,y
+ inx
+ iny
+Start_map:
+ cpx #__data_image_end
+ blo Loop
+#endif
+Done:
+
+#endif
+
+#ifdef L_init_bss
+
+ .sect .install2,"ax",@progbits
+ .globl __init_bss_section
+
+__init_bss_section:
+ ldd #__bss_size
+ beq Done
+ ldx #__bss_start
+Loop:
+#ifdef mc68hc12
+ clr 1,x+
+ dbne d,Loop
+#else
+ clr 0,x
+ inx
+ subd #1
+ bne Loop
+#endif
+Done:
+
+#endif
+
+#ifdef L_ctor
+
+; End of constructor table
+ .sect .install3,"ax",@progbits
+ .globl __do_global_ctors
+
+__do_global_ctors:
+ ; Start from the end - sizeof(void*)
+ ldx #__CTOR_END__-2
+ctors_loop:
+ cpx #__CTOR_LIST__
+ blo ctors_done
+ pshx
+ ldx 0,x
+ jsr 0,x
+ pulx
+ dex
+ dex
+ bra ctors_loop
+ctors_done:
+
+#endif
+
+#ifdef L_dtor
+
+ .sect .fini3,"ax",@progbits
+ .globl __do_global_dtors
+
+;;
+;; This piece of code is inserted in the _exit() code by the linker.
+;;
+__do_global_dtors:
+ pshb ; Save exit code
+ psha
+ ldx #__DTOR_LIST__
+dtors_loop:
+ cpx #__DTOR_END__
+ bhs dtors_done
+ pshx
+ ldx 0,x
+ jsr 0,x
+ pulx
+ inx
+ inx
+ bra dtors_loop
+dtors_done:
+ pula ; Restore exit code
+ pulb
+
+#endif
+
+#ifdef L_far_tramp
+#ifdef mc68hc12
+ .sect .tramp,"ax",@progbits
+ .globl __far_trampoline
+
+;; This is a trampoline used by the linker to invoke a function
+;; using rtc to return and being called with jsr/bsr.
+;; The trampoline generated is:
+;;
+;; foo_tramp:
+;; ldy #foo
+;; call __far_trampoline,page(foo)
+;;
+;; The linker transforms:
+;;
+;; jsr foo
+;;
+;; into
+;; jsr foo_tramp
+;;
+;; The linker generated trampoline and _far_trampoline must be in
+;; non-banked memory.
+;;
+__far_trampoline:
+ movb 0,sp, 2,sp ; Copy page register below the caller's return
+ leas 2,sp ; address.
+ jmp 0,y ; We have a 'call/rtc' stack layout now
+ ; and can jump to the far handler
+ ; (whose memory bank is mapped due to the
+ ; call to the trampoline).
+#endif
+
+#ifdef mc68hc11
+ .sect .tramp,"ax",@progbits
+ .globl __far_trampoline
+
+;; Trampoline generated by gcc for 68HC11:
+;;
+;; pshb
+;; ldab #%page(func)
+;; ldy #%addr(func)
+;; jmp __far_trampoline
+;;
+__far_trampoline:
+ psha ; (2) Save function parameter (high)
+ ;; <Read current page in A>
+ psha ; (2)
+ ;; <Set currenge page from B>
+ pshx ; (4)
+ tsx ; (3)
+ ldab 4,x ; (4) Restore function parameter (low)
+ ldaa 2,x ; (4) Get saved page number
+ staa 4,x ; (4) Save it below return PC
+ pulx ; (5)
+ pula ; (3)
+ pula ; (3) Restore function parameter (high)
+ jmp 0,y ; (4)
+#endif
+#endif
+
+#ifdef L_call_far
+#ifdef mc68hc11
+ .sect .tramp,"ax",@progbits
+ .globl __call_a16
+ .globl __call_a32
+;;
+;; The call methods are used for 68HC11 to support memory bank switching.
+;; Every far call is redirected to these call methods. Its purpose is to:
+;;
+;; 1/ Save the current page on the stack (1 byte to follow 68HC12 call frame)
+;; 2/ Install the new page
+;; 3/ Jump to the real function
+;;
+;; The page switching (get/save) is board dependent. The default provided
+;; here does nothing (just create the appropriate call frame).
+;;
+;; Call sequence (10 bytes, 13 cycles):
+;;
+;; ldx #page ; (3)
+;; ldy #func ; (4)
+;; jsr __call_a16 ; (6)
+;;
+;; Call trampoline (11 bytes, 19 cycles):
+;;
+__call_a16:
+ ;; xgdx ; (3)
+ ;; <Read current page in A> ; (3) ldaa _current_page
+ psha ; (2)
+ ;; <Set current page from B> ; (4) staa _current_page
+ ;; xgdx ; (3)
+ jmp 0,y ; (4)
+
+;;
+;; Call sequence (10 bytes, 14 cycles):
+;;
+;; pshb ; (2)
+;; ldab #page ; (2)
+;; ldy #func ; (4)
+;; jsr __call_a32 ; (6)
+;;
+;; Call trampoline (87 bytes, 57 cycles):
+;;
+__call_a32:
+ pshx ; (4)
+ psha ; (2)
+ ;; <Read current page in A> ; (3) ldaa _current_page
+ psha ; (2)
+ ;; <Set current page from B> ; (4) staa _current_page
+ tsx ; (3)
+ ldab 6,x ; (4) Restore function parameter
+ ldaa 5,x ; (4) Move PC return at good place
+ staa 6,x ; (4)
+ ldaa 4,x ; (4)
+ staa 5,x ; (4)
+ pula ; (3)
+ staa 4,x ; (4)
+ pula ; (3)
+ pulx ; (5)
+ jmp 0,y ; (4)
+#endif
+#endif
+
+#ifdef L_return_far
+#ifdef mc68hc11
+ .sect .tramp,"ax",@progbits
+ .globl __return_void
+ .globl __return_16
+ .globl __return_32
+
+__return_void:
+ ;; pulb
+ ;; <Set current page from B> (Board specific)
+ ;; rts
+__return_16:
+ ;; xgdx
+ ;; pulb
+ ;; <Set current page from B> (Board specific)
+ ;; xgdx
+ ;; rts
+__return_32:
+ ;; xgdy
+ ;; pulb
+ ;; <Set current page from B> (Board specific)
+ ;; xgdy
+ ;; rts
+ ins
+ rts
+#endif
+#endif
+.Lend:
+;-----------------------------------------
+; end required gcclib code
+;-----------------------------------------
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11-crt0.S b/gcc-4.4.0/gcc/config/m68hc11/m68hc11-crt0.S
new file mode 100644
index 000000000..429ab0f27
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11-crt0.S
@@ -0,0 +1,86 @@
+/* Startup code for M68HC11.
+ Copyright (C) 1999, 2000, 2002, 2008, 2009 Free Software Foundation, Inc.
+
+This file 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.
+
+This file 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+;-----------------------------------------
+; startup code
+;-----------------------------------------
+
+#ifdef __HAVE_SHORT_INT__
+ .mode mshort
+#else
+ .mode mlong
+#endif
+
+#if defined(__USE_RTC__) && defined(mc68hc12)
+ .macro jsr name
+ call \name
+ .endm
+#endif
+;;
+;;
+;; The linker concatenate the .install* sections in the following order:
+;;
+;; .install0 Setup the stack pointer
+;; .install1 Place holder for applications
+;; .install2 Optional installation of data section in memory
+;; .install3 Place holder for applications
+;; .install4 Invokes the main
+;;
+ .sect .install0,"ax",@progbits
+ .globl _start
+
+_start:
+;;
+;; At this step, the stack is not initialized and interrupts are masked.
+;; Applications only have 64 cycles to initialize some registers.
+;;
+;; To have a generic/configurable startup, initialize the stack to
+;; the end of some memory region. The _stack symbol is defined by
+;; the linker.
+;;
+ lds #_stack
+
+ .sect .install2,"ax",@progbits
+;;
+;; Call a specific initialization operation. The default is empty.
+;; It can be overridden by applications. It is intended to initialize
+;; the 68hc11 registers. Function prototype is:
+;;
+;; int __premain(void);
+;;
+ jsr __premain
+
+;;
+;;
+;;
+ .sect .install4,"ax",@progbits
+ jsr main
+fatal:
+ jsr exit
+ bra fatal
+
+;-----------------------------------------
+; end startup code
+;-----------------------------------------
+;; Force loading of data section mapping and bss clear
+ .2byte __map_data_section
+ .2byte __init_bss_section
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11-protos.h b/gcc-4.4.0/gcc/config/m68hc11/m68hc11-protos.h
new file mode 100644
index 000000000..1e9cf5859
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11-protos.h
@@ -0,0 +1,130 @@
+/* Prototypes for exported functions defined in m68hc11.c
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
+ 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/>. */
+
+
+extern int m68hc11_override_options (void);
+extern int m68hc11_optimization_options (int,int);
+extern void m68hc11_conditional_register_usage (void);
+extern int hard_regno_mode_ok (int, enum machine_mode);
+extern int m68hc11_hard_regno_rename_ok (int, int);
+
+extern int m68hc11_total_frame_size (void);
+extern int m68hc11_initial_frame_pointer_offset (void);
+extern int m68hc11_initial_elimination_offset (int, int);
+
+extern void expand_prologue (void);
+extern void expand_epilogue (void);
+
+#ifdef TREE_CODE
+extern void m68hc11_function_arg_advance (CUMULATIVE_ARGS*,
+ enum machine_mode,
+ tree,
+ int);
+#endif
+
+#ifdef RTX_CODE
+extern int m68hc11_auto_inc_p (rtx);
+
+extern void m68hc11_initialize_trampoline (rtx, rtx, rtx);
+
+extern rtx m68hc11_expand_compare_and_branch (enum rtx_code, rtx, rtx, rtx);
+extern enum reg_class preferred_reload_class (rtx, enum reg_class);
+
+extern int m68hc11_go_if_legitimate_address (rtx, enum machine_mode, int);
+
+extern int m68hc11_legitimize_address (rtx*, rtx, enum machine_mode);
+
+extern void m68hc11_notice_update_cc (rtx, rtx);
+extern void m68hc11_notice_keep_cc (rtx);
+
+extern void m68hc11_gen_movqi (rtx, rtx*);
+extern void m68hc11_gen_movhi (rtx, rtx*);
+extern void m68hc11_gen_rotate (enum rtx_code, rtx, rtx*);
+
+extern void m68hc11_output_swap (rtx, rtx*);
+
+extern int next_insn_test_reg (rtx, rtx);
+
+extern void print_operand (FILE*, rtx, int);
+extern void print_operand_address (FILE*, rtx);
+
+extern int m68hc11_reload_operands (rtx*);
+
+extern int dead_register_here (rtx, rtx);
+
+extern int push_pop_operand_p (rtx);
+extern void m68hc11_split_move (rtx, rtx, rtx);
+extern void m68hc11_split_compare_and_branch (enum rtx_code,
+ rtx, rtx, rtx);
+
+extern rtx m68hc11_gen_lowpart (enum machine_mode, rtx);
+extern rtx m68hc11_gen_highpart (enum machine_mode, rtx);
+
+#ifdef HAVE_MACHINE_MODES
+extern int m68hc11_memory_move_cost (enum machine_mode, enum reg_class, int);
+extern int m68hc11_register_move_cost (enum machine_mode,
+ enum reg_class, enum reg_class);
+
+extern void m68hc11_emit_libcall (const char*, enum rtx_code,
+ enum machine_mode, enum machine_mode,
+ int, rtx*);
+extern int m68hc11_small_indexed_indirect_p (rtx, enum machine_mode);
+extern int m68hc11_symbolic_p (rtx, enum machine_mode);
+extern int m68hc11_indirect_p (rtx, enum machine_mode);
+extern int go_if_legitimate_address2 (rtx, enum machine_mode, int);
+
+extern int reg_or_indexed_operand (rtx,enum machine_mode);
+extern int memory_indexed_operand (rtx, enum machine_mode);
+
+extern void m68hc11_split_logical (enum machine_mode, int, rtx*);
+
+extern int m68hc11_register_indirect_p (rtx, enum machine_mode);
+extern int m68hc11_valid_addressing_p (rtx, enum machine_mode, int);
+
+extern int symbolic_memory_operand (rtx, enum machine_mode);
+
+extern int memory_reload_operand (rtx, enum machine_mode);
+extern int arith_src_operand (rtx, enum machine_mode);
+extern int soft_reg_operand (rtx, enum machine_mode);
+
+#if defined TREE_CODE
+extern void m68hc11_init_cumulative_args (CUMULATIVE_ARGS*, tree, rtx);
+
+extern rtx m68hc11_function_arg (const CUMULATIVE_ARGS* ,
+ enum machine_mode,
+ tree, int);
+extern int m68hc11_function_arg_padding (enum machine_mode, const_tree);
+
+extern void m68hc11_function_epilogue (FILE*,int);
+
+extern int m68hc11_is_far_symbol (rtx);
+extern int m68hc11_is_trap_symbol (rtx);
+extern int m68hc11_page0_symbol_p (rtx x);
+
+#endif /* TREE_CODE */
+
+extern HOST_WIDE_INT m68hc11_min_offset;
+extern HOST_WIDE_INT m68hc11_max_offset;
+extern int m68hc11_addr_mode;
+
+#endif /* HAVE_MACHINE_MODES */
+#endif /* RTX_CODE */
+
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11.c b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.c
new file mode 100644
index 000000000..4fba4a39a
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.c
@@ -0,0 +1,5499 @@
+/* Subroutines for code generation on Motorola 68HC11 and 68HC12.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 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
+
+*/
+
+#include <stdio.h>
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "tree.h"
+#include "tm_p.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "recog.h"
+#include "expr.h"
+#include "libfuncs.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "function.h"
+#include "ggc.h"
+#include "reload.h"
+#include "target.h"
+#include "target-def.h"
+#include "df.h"
+
+static void emit_move_after_reload (rtx, rtx, rtx);
+static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
+static void m68hc11_emit_logical (enum machine_mode, int, rtx *);
+static void m68hc11_reorg (void);
+static int go_if_legitimate_address_internal (rtx, enum machine_mode, int);
+static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
+static int must_parenthesize (rtx);
+static int m68hc11_address_cost (rtx, bool);
+static int m68hc11_shift_cost (enum machine_mode, rtx, int);
+static int m68hc11_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
+static bool m68hc11_rtx_costs (rtx, int, int, int *, bool);
+static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
+const struct attribute_spec m68hc11_attribute_table[];
+
+void create_regs_rtx (void);
+
+static void asm_print_register (FILE *, int);
+static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static void m68hc11_asm_out_constructor (rtx, int);
+static void m68hc11_asm_out_destructor (rtx, int);
+static void m68hc11_file_start (void);
+static void m68hc11_encode_section_info (tree, rtx, int);
+static const char *m68hc11_strip_name_encoding (const char* str);
+static unsigned int m68hc11_section_type_flags (tree, const char*, int);
+static int autoinc_mode (rtx);
+static int m68hc11_make_autoinc_notes (rtx *, void *);
+static void m68hc11_init_libfuncs (void);
+static rtx m68hc11_struct_value_rtx (tree, int);
+static bool m68hc11_return_in_memory (const_tree, const_tree);
+
+/* Must be set to 1 to produce debug messages. */
+int debug_m6811 = 0;
+
+extern FILE *asm_out_file;
+
+rtx ix_reg;
+rtx iy_reg;
+rtx d_reg;
+rtx m68hc11_soft_tmp_reg;
+static GTY(()) rtx stack_push_word;
+static GTY(()) rtx stack_pop_word;
+static GTY(()) rtx z_reg;
+static GTY(()) rtx z_reg_qi;
+static int regs_inited = 0;
+
+/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
+int current_function_interrupt;
+
+/* Set to 1 by expand_prologue() when the function is a trap handler. */
+int current_function_trap;
+
+/* Set to 1 when the current function is placed in 68HC12 banked
+ memory and must return with rtc. */
+int current_function_far;
+
+/* Min offset that is valid for the indirect addressing mode. */
+HOST_WIDE_INT m68hc11_min_offset = 0;
+
+/* Max offset that is valid for the indirect addressing mode. */
+HOST_WIDE_INT m68hc11_max_offset = 256;
+
+/* The class value for base registers. */
+enum reg_class m68hc11_base_reg_class = A_REGS;
+
+/* The class value for index registers. This is NO_REGS for 68HC11. */
+enum reg_class m68hc11_index_reg_class = NO_REGS;
+
+enum reg_class m68hc11_tmp_regs_class = NO_REGS;
+
+/* Tables that tell whether a given hard register is valid for
+ a base or an index register. It is filled at init time depending
+ on the target processor. */
+unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
+unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
+
+/* A correction offset which is applied to the stack pointer.
+ This is 1 for 68HC11 and 0 for 68HC12. */
+int m68hc11_sp_correction;
+
+int m68hc11_addr_mode;
+int m68hc11_mov_addr_mode;
+
+/* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
+rtx m68hc11_compare_op0;
+rtx m68hc11_compare_op1;
+
+
+const struct processor_costs *m68hc11_cost;
+
+/* Costs for a 68HC11. */
+static const struct processor_costs m6811_cost = {
+ /* add */
+ COSTS_N_INSNS (2),
+ /* logical */
+ COSTS_N_INSNS (2),
+ /* non-constant shift */
+ COSTS_N_INSNS (20),
+ /* shiftQI const */
+ { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+ COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
+ COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
+
+ /* shiftHI const */
+ { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
+ COSTS_N_INSNS (4), COSTS_N_INSNS (2),
+ COSTS_N_INSNS (2), COSTS_N_INSNS (4),
+ COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
+ COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
+ },
+ /* mulQI */
+ COSTS_N_INSNS (20),
+ /* mulHI */
+ COSTS_N_INSNS (20 * 4),
+ /* mulSI */
+ COSTS_N_INSNS (20 * 16),
+ /* divQI */
+ COSTS_N_INSNS (20),
+ /* divHI */
+ COSTS_N_INSNS (80),
+ /* divSI */
+ COSTS_N_INSNS (100)
+};
+
+/* Costs for a 68HC12. */
+static const struct processor_costs m6812_cost = {
+ /* add */
+ COSTS_N_INSNS (2),
+ /* logical */
+ COSTS_N_INSNS (2),
+ /* non-constant shift */
+ COSTS_N_INSNS (20),
+ /* shiftQI const */
+ { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+ COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
+ COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
+
+ /* shiftHI const */
+ { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
+ COSTS_N_INSNS (4), COSTS_N_INSNS (2),
+ COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
+ COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
+ COSTS_N_INSNS (6), COSTS_N_INSNS (4)
+ },
+ /* mulQI */
+ COSTS_N_INSNS (3),
+ /* mulHI */
+ COSTS_N_INSNS (3),
+ /* mulSI */
+ COSTS_N_INSNS (3 * 4),
+ /* divQI */
+ COSTS_N_INSNS (12),
+ /* divHI */
+ COSTS_N_INSNS (12),
+ /* divSI */
+ COSTS_N_INSNS (100)
+};
+
+/* Initialize the GCC target structure. */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
+
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START m68hc11_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
+
+#undef TARGET_SECTION_TYPE_FLAGS
+#define TARGET_SECTION_TYPE_FLAGS m68hc11_section_type_flags
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS m68hc11_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST m68hc11_address_cost
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS m68hc11_init_libfuncs
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX m68hc11_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY m68hc11_return_in_memory
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES hook_callee_copies_named
+
+#undef TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+int
+m68hc11_override_options (void)
+{
+ memset (m68hc11_reg_valid_for_index, 0,
+ sizeof (m68hc11_reg_valid_for_index));
+ memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
+
+ /* Compilation with -fpic generates a wrong code. */
+ if (flag_pic)
+ {
+ warning (0, "-f%s ignored for 68HC11/68HC12 (not supported)",
+ (flag_pic > 1) ? "PIC" : "pic");
+ flag_pic = 0;
+ }
+
+ /* Do not enable -fweb because it breaks the 32-bit shift patterns
+ by breaking the match_dup of those patterns. The shift patterns
+ will no longer be recognized after that. */
+ flag_web = 0;
+
+ /* Configure for a 68hc11 processor. */
+ if (TARGET_M6811)
+ {
+ target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
+ m68hc11_cost = &m6811_cost;
+ m68hc11_min_offset = 0;
+ m68hc11_max_offset = 256;
+ m68hc11_index_reg_class = NO_REGS;
+ m68hc11_base_reg_class = A_REGS;
+ m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
+ m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
+ m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
+ m68hc11_sp_correction = 1;
+ m68hc11_tmp_regs_class = D_REGS;
+ m68hc11_addr_mode = ADDR_OFFSET;
+ m68hc11_mov_addr_mode = 0;
+ if (m68hc11_soft_reg_count < 0)
+ m68hc11_soft_reg_count = 4;
+ }
+
+ /* Configure for a 68hc12 processor. */
+ if (TARGET_M6812)
+ {
+ m68hc11_cost = &m6812_cost;
+ m68hc11_min_offset = -65536;
+ m68hc11_max_offset = 65536;
+ m68hc11_index_reg_class = D_REGS;
+ m68hc11_base_reg_class = A_OR_SP_REGS;
+ m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
+ m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
+ m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
+ m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
+ m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
+ m68hc11_sp_correction = 0;
+ m68hc11_tmp_regs_class = TMP_REGS;
+ m68hc11_addr_mode = ADDR_INDIRECT | ADDR_OFFSET | ADDR_CONST
+ | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
+ m68hc11_mov_addr_mode = ADDR_OFFSET | ADDR_CONST
+ | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
+ target_flags |= MASK_NO_DIRECT_MODE;
+ if (m68hc11_soft_reg_count < 0)
+ m68hc11_soft_reg_count = 0;
+
+ if (TARGET_LONG_CALLS)
+ current_function_far = 1;
+ }
+ return 0;
+}
+
+
+void
+m68hc11_conditional_register_usage (void)
+{
+ int i;
+
+ if (m68hc11_soft_reg_count > SOFT_REG_LAST - SOFT_REG_FIRST)
+ m68hc11_soft_reg_count = SOFT_REG_LAST - SOFT_REG_FIRST;
+
+ for (i = SOFT_REG_FIRST + m68hc11_soft_reg_count; i < SOFT_REG_LAST; i++)
+ {
+ fixed_regs[i] = 1;
+ call_used_regs[i] = 1;
+ }
+
+ /* For 68HC12, the Z register emulation is not necessary when the
+ frame pointer is not used. The frame pointer is eliminated and
+ replaced by the stack register (which is a BASE_REG_CLASS). */
+ if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
+ {
+ fixed_regs[HARD_Z_REGNUM] = 1;
+ }
+}
+
+
+/* Reload and register operations. */
+
+
+void
+create_regs_rtx (void)
+{
+ /* regs_inited = 1; */
+ ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
+ d_reg = gen_rtx_REG (HImode, HARD_D_REGNUM);
+ m68hc11_soft_tmp_reg = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
+
+ stack_push_word = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ stack_pop_word = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+
+}
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+ - 8-bit values are stored anywhere (except the SP register).
+ - 16-bit values can be stored in any register whose mode is 16
+ - 32-bit values can be stored in D, X registers or in a soft register
+ (except the last one because we need 2 soft registers)
+ - Values whose size is > 32 bit are not stored in real hard
+ registers. They may be stored in soft registers if there are
+ enough of them. */
+int
+hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 8:
+ return S_REGNO_P (regno) && m68hc11_soft_reg_count >= 4;
+
+ case 4:
+ return (X_REGNO_P (regno)
+ || (S_REGNO_P (regno) && m68hc11_soft_reg_count >= 2));
+
+ case 2:
+ return G_REGNO_P (regno);
+
+ case 1:
+ /* We have to accept a QImode in X or Y registers. Otherwise, the
+ reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
+ in the insns. Reload fails if the insn rejects the register class 'a'
+ as well as if it accepts it. Patterns that failed were
+ zero_extend_qihi2 and iorqi3. */
+
+ return G_REGNO_P (regno) && !SP_REGNO_P (regno);
+
+ default:
+ return 0;
+ }
+}
+
+int
+m68hc11_hard_regno_rename_ok (int reg1, int reg2)
+{
+ /* Don't accept renaming to Z register. We will replace it to
+ X,Y or D during machine reorg pass. */
+ if (reg2 == HARD_Z_REGNUM)
+ return 0;
+
+ /* Don't accept renaming D,X to Y register as the code will be bigger. */
+ if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
+ && (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
+ return 0;
+
+ return 1;
+}
+
+enum reg_class
+preferred_reload_class (rtx operand, enum reg_class rclass)
+{
+ enum machine_mode mode;
+
+ mode = GET_MODE (operand);
+
+ if (debug_m6811)
+ {
+ printf ("Preferred reload: (class=%s): ", reg_class_names[rclass]);
+ }
+
+ if (rclass == D_OR_A_OR_S_REGS && SP_REG_P (operand))
+ return m68hc11_base_reg_class;
+
+ if (rclass >= S_REGS && (GET_CODE (operand) == MEM
+ || GET_CODE (operand) == CONST_INT))
+ {
+ /* S_REGS class must not be used. The movhi template does not
+ work to move a memory to a soft register.
+ Restrict to a hard reg. */
+ switch (rclass)
+ {
+ default:
+ case G_REGS:
+ case D_OR_A_OR_S_REGS:
+ rclass = A_OR_D_REGS;
+ break;
+ case A_OR_S_REGS:
+ rclass = A_REGS;
+ break;
+ case D_OR_SP_OR_S_REGS:
+ rclass = D_OR_SP_REGS;
+ break;
+ case D_OR_Y_OR_S_REGS:
+ rclass = D_OR_Y_REGS;
+ break;
+ case D_OR_X_OR_S_REGS:
+ rclass = D_OR_X_REGS;
+ break;
+ case SP_OR_S_REGS:
+ rclass = SP_REGS;
+ break;
+ case Y_OR_S_REGS:
+ rclass = Y_REGS;
+ break;
+ case X_OR_S_REGS:
+ rclass = X_REGS;
+ break;
+ case D_OR_S_REGS:
+ rclass = D_REGS;
+ }
+ }
+ else if (rclass == Y_REGS && GET_CODE (operand) == MEM)
+ {
+ rclass = Y_REGS;
+ }
+ else if (rclass == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
+ {
+ rclass = D_OR_X_REGS;
+ }
+ else if (rclass >= S_REGS && S_REG_P (operand))
+ {
+ switch (rclass)
+ {
+ default:
+ case G_REGS:
+ case D_OR_A_OR_S_REGS:
+ rclass = A_OR_D_REGS;
+ break;
+ case A_OR_S_REGS:
+ rclass = A_REGS;
+ break;
+ case D_OR_SP_OR_S_REGS:
+ rclass = D_OR_SP_REGS;
+ break;
+ case D_OR_Y_OR_S_REGS:
+ rclass = D_OR_Y_REGS;
+ break;
+ case D_OR_X_OR_S_REGS:
+ rclass = D_OR_X_REGS;
+ break;
+ case SP_OR_S_REGS:
+ rclass = SP_REGS;
+ break;
+ case Y_OR_S_REGS:
+ rclass = Y_REGS;
+ break;
+ case X_OR_S_REGS:
+ rclass = X_REGS;
+ break;
+ case D_OR_S_REGS:
+ rclass = D_REGS;
+ }
+ }
+ else if (rclass >= S_REGS)
+ {
+ if (debug_m6811)
+ {
+ printf ("Class = %s for: ", reg_class_names[rclass]);
+ fflush (stdout);
+ debug_rtx (operand);
+ }
+ }
+
+ if (debug_m6811)
+ {
+ printf (" => class=%s\n", reg_class_names[rclass]);
+ fflush (stdout);
+ debug_rtx (operand);
+ }
+
+ return rclass;
+}
+
+/* Return 1 if the operand is a valid indexed addressing mode.
+ For 68hc11: n,r with n in [0..255] and r in A_REGS class
+ For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
+int
+m68hc11_valid_addressing_p (rtx operand, enum machine_mode mode, int addr_mode)
+{
+ rtx base, offset;
+
+ switch (GET_CODE (operand))
+ {
+ case MEM:
+ if ((addr_mode & ADDR_INDIRECT) && GET_MODE_SIZE (mode) <= 2)
+ return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
+ addr_mode & (ADDR_STRICT | ADDR_OFFSET));
+ return 0;
+
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ if (addr_mode & ADDR_INCDEC)
+ return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
+ addr_mode & ADDR_STRICT);
+ return 0;
+
+ case PLUS:
+ base = XEXP (operand, 0);
+ if (GET_CODE (base) == MEM)
+ return 0;
+
+ offset = XEXP (operand, 1);
+ if (GET_CODE (offset) == MEM)
+ return 0;
+
+ /* Indexed addressing mode with 2 registers. */
+ if (GET_CODE (base) == REG && GET_CODE (offset) == REG)
+ {
+ if (!(addr_mode & ADDR_INDEXED))
+ return 0;
+
+ addr_mode &= ADDR_STRICT;
+ if (REGNO_OK_FOR_BASE_P2 (REGNO (base), addr_mode)
+ && REGNO_OK_FOR_INDEX_P2 (REGNO (offset), addr_mode))
+ return 1;
+
+ if (REGNO_OK_FOR_BASE_P2 (REGNO (offset), addr_mode)
+ && REGNO_OK_FOR_INDEX_P2 (REGNO (base), addr_mode))
+ return 1;
+
+ return 0;
+ }
+
+ if (!(addr_mode & ADDR_OFFSET))
+ return 0;
+
+ if (GET_CODE (base) == REG)
+ {
+ if (!VALID_CONSTANT_OFFSET_P (offset, mode))
+ return 0;
+
+ if (!(addr_mode & ADDR_STRICT))
+ return 1;
+
+ return REGNO_OK_FOR_BASE_P2 (REGNO (base), 1);
+ }
+
+ if (GET_CODE (offset) == REG)
+ {
+ if (!VALID_CONSTANT_OFFSET_P (base, mode))
+ return 0;
+
+ if (!(addr_mode & ADDR_STRICT))
+ return 1;
+
+ return REGNO_OK_FOR_BASE_P2 (REGNO (offset), 1);
+ }
+ return 0;
+
+ case REG:
+ return REGNO_OK_FOR_BASE_P2 (REGNO (operand), addr_mode & ADDR_STRICT);
+
+ case CONST_INT:
+ if (addr_mode & ADDR_CONST)
+ return VALID_CONSTANT_OFFSET_P (operand, mode);
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns 1 if the operand fits in a 68HC11 indirect mode or in
+ a 68HC12 1-byte index addressing mode. */
+int
+m68hc11_small_indexed_indirect_p (rtx operand, enum machine_mode mode)
+{
+ rtx base, offset;
+ int addr_mode;
+
+ if (GET_CODE (operand) == REG && reload_in_progress
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_memory_loc[REGNO (operand)])
+ {
+ operand = reg_equiv_memory_loc[REGNO (operand)];
+ operand = eliminate_regs (operand, 0, NULL_RTX);
+ }
+
+ if (GET_CODE (operand) != MEM)
+ return 0;
+
+ operand = XEXP (operand, 0);
+ if (CONSTANT_ADDRESS_P (operand))
+ return 1;
+
+ if (PUSH_POP_ADDRESS_P (operand))
+ return 1;
+
+ addr_mode = m68hc11_mov_addr_mode | (reload_completed ? ADDR_STRICT : 0);
+ if (!m68hc11_valid_addressing_p (operand, mode, addr_mode))
+ return 0;
+
+ if (TARGET_M6812 && GET_CODE (operand) == PLUS
+ && (reload_completed | reload_in_progress))
+ {
+ base = XEXP (operand, 0);
+ offset = XEXP (operand, 1);
+
+ /* The offset can be a symbol address and this is too big
+ for the operand constraint. */
+ if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
+ return 0;
+
+ if (GET_CODE (base) == CONST_INT)
+ offset = base;
+
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 8:
+ if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
+ return 0;
+ break;
+
+ case 4:
+ if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
+ return 0;
+ break;
+
+ default:
+ if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
+ return 0;
+ break;
+ }
+ }
+ return 1;
+}
+
+int
+m68hc11_register_indirect_p (rtx operand, enum machine_mode mode)
+{
+ int addr_mode;
+
+ if (GET_CODE (operand) == REG && reload_in_progress
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_memory_loc[REGNO (operand)])
+ {
+ operand = reg_equiv_memory_loc[REGNO (operand)];
+ operand = eliminate_regs (operand, 0, NULL_RTX);
+ }
+ if (GET_CODE (operand) != MEM)
+ return 0;
+
+ operand = XEXP (operand, 0);
+ addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
+ return m68hc11_valid_addressing_p (operand, mode, addr_mode);
+}
+
+static int
+go_if_legitimate_address_internal (rtx operand, enum machine_mode mode,
+ int strict)
+{
+ int addr_mode;
+
+ if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
+ {
+ /* Reject the global variables if they are too wide. This forces
+ a load of their address in a register and generates smaller code. */
+ if (GET_MODE_SIZE (mode) == 8)
+ return 0;
+
+ return 1;
+ }
+ addr_mode = m68hc11_addr_mode | (strict ? ADDR_STRICT : 0);
+ if (m68hc11_valid_addressing_p (operand, mode, addr_mode))
+ {
+ return 1;
+ }
+ if (PUSH_POP_ADDRESS_P (operand))
+ {
+ return 1;
+ }
+ if (symbolic_memory_operand (operand, mode))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int
+m68hc11_go_if_legitimate_address (rtx operand, enum machine_mode mode,
+ int strict)
+{
+ int result;
+
+ if (debug_m6811)
+ {
+ printf ("Checking: ");
+ fflush (stdout);
+ debug_rtx (operand);
+ }
+
+ result = go_if_legitimate_address_internal (operand, mode, strict);
+
+ if (debug_m6811)
+ {
+ printf (" -> %s\n", result == 0 ? "NO" : "YES");
+ }
+
+ if (result == 0)
+ {
+ if (debug_m6811)
+ {
+ printf ("go_if_legitimate%s, ret 0: %d:",
+ (strict ? "_strict" : ""), mode);
+ fflush (stdout);
+ debug_rtx (operand);
+ }
+ }
+ return result;
+}
+
+int
+m68hc11_legitimize_address (rtx *operand ATTRIBUTE_UNUSED,
+ rtx old_operand ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+
+int
+m68hc11_reload_operands (rtx operands[])
+{
+ enum machine_mode mode;
+
+ if (regs_inited == 0)
+ create_regs_rtx ();
+
+ mode = GET_MODE (operands[1]);
+
+ /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
+ if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
+ {
+ rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
+ rtx base = XEXP (XEXP (operands[1], 0), 0);
+
+ if (GET_CODE (base) != REG)
+ {
+ rtx tmp = base;
+ base = big_offset;
+ big_offset = tmp;
+ }
+
+ /* If the offset is out of range, we have to compute the address
+ with a separate add instruction. We try to do this with an 8-bit
+ add on the A register. This is possible only if the lowest part
+ of the offset (i.e., big_offset % 256) is a valid constant offset
+ with respect to the mode. If it's not, we have to generate a
+ 16-bit add on the D register. From:
+
+ (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
+
+ we generate:
+
+ [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
+ (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
+ [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
+ (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
+
+ (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
+ (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
+
+ */
+ if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
+ {
+ int vh, vl;
+ rtx reg = operands[0];
+ rtx offset;
+ int val = INTVAL (big_offset);
+
+
+ /* We use the 'operands[0]' as a scratch register to compute the
+ address. Make sure 'base' is in that register. */
+ if (!rtx_equal_p (base, operands[0]))
+ {
+ emit_move_insn (reg, base);
+ }
+
+ if (val > 0)
+ {
+ vh = val >> 8;
+ vl = val & 0x0FF;
+ }
+ else
+ {
+ vh = (val >> 8) & 0x0FF;
+ vl = val & 0x0FF;
+ }
+
+ /* Create the lowest part offset that still remains to be added.
+ If it's not a valid offset, do a 16-bit add. */
+ offset = GEN_INT (vl);
+ if (!VALID_CONSTANT_OFFSET_P (offset, mode))
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (HImode, reg, big_offset)));
+ offset = const0_rtx;
+ }
+ else
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (HImode, reg,
+ GEN_INT (vh << 8))));
+ }
+ emit_move_insn (operands[0],
+ gen_rtx_MEM (GET_MODE (operands[1]),
+ gen_rtx_PLUS (Pmode, reg, offset)));
+ return 1;
+ }
+ }
+
+ /* Use the normal gen_movhi pattern. */
+ return 0;
+}
+
+void
+m68hc11_emit_libcall (const char *name, enum rtx_code code,
+ enum machine_mode dmode, enum machine_mode smode,
+ int noperands, rtx *operands)
+{
+ rtx ret;
+ rtx insns;
+ rtx libcall;
+ rtx equiv;
+
+ start_sequence ();
+ libcall = gen_rtx_SYMBOL_REF (Pmode, name);
+ switch (noperands)
+ {
+ case 2:
+ ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
+ dmode, 1, operands[1], smode);
+ equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
+ break;
+
+ case 3:
+ ret = emit_library_call_value (libcall, NULL_RTX,
+ LCT_CONST, dmode, 2,
+ operands[1], smode, operands[2],
+ smode);
+ equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+ emit_libcall_block (insns, operands[0], ret, equiv);
+}
+
+/* Returns true if X is a PRE/POST increment decrement
+ (same as auto_inc_p() in rtlanal.c but do not take into
+ account the stack). */
+int
+m68hc11_auto_inc_p (rtx x)
+{
+ return GET_CODE (x) == PRE_DEC
+ || GET_CODE (x) == POST_INC
+ || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
+}
+
+
+/* Predicates for machine description. */
+
+int
+memory_reload_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return GET_CODE (operand) == MEM
+ && GET_CODE (XEXP (operand, 0)) == PLUS
+ && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
+ || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
+ && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
+}
+
+int
+m68hc11_symbolic_p (rtx operand, enum machine_mode mode)
+{
+ if (GET_CODE (operand) == MEM)
+ {
+ rtx op = XEXP (operand, 0);
+
+ if (symbolic_memory_operand (op, mode))
+ return 1;
+ }
+ return 0;
+}
+
+int
+m68hc11_indirect_p (rtx operand, enum machine_mode mode)
+{
+ if (GET_CODE (operand) == MEM && GET_MODE (operand) == mode)
+ {
+ rtx op = XEXP (operand, 0);
+ int addr_mode;
+
+ if (m68hc11_page0_symbol_p (op))
+ return 1;
+
+ if (symbolic_memory_operand (op, mode))
+ return TARGET_M6812;
+
+ if (reload_in_progress)
+ return 1;
+
+ operand = XEXP (operand, 0);
+ addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
+ return m68hc11_valid_addressing_p (operand, mode, addr_mode);
+ }
+ return 0;
+}
+
+int
+memory_indexed_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (operand) != MEM)
+ return 0;
+
+ operand = XEXP (operand, 0);
+ if (GET_CODE (operand) == PLUS)
+ {
+ if (GET_CODE (XEXP (operand, 0)) == REG)
+ operand = XEXP (operand, 0);
+ else if (GET_CODE (XEXP (operand, 1)) == REG)
+ operand = XEXP (operand, 1);
+ }
+ return GET_CODE (operand) == REG
+ && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ || A_REGNO_P (REGNO (operand)));
+}
+
+int
+push_pop_operand_p (rtx operand)
+{
+ if (GET_CODE (operand) != MEM)
+ {
+ return 0;
+ }
+ operand = XEXP (operand, 0);
+ return PUSH_POP_ADDRESS_P (operand);
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
+
+int
+symbolic_memory_operand (rtx op, enum machine_mode mode)
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
+
+ /* ??? This clause seems to be irrelevant. */
+ case CONST_DOUBLE:
+ return GET_MODE (op) == mode;
+
+ case PLUS:
+ return symbolic_memory_operand (XEXP (op, 0), mode)
+ && symbolic_memory_operand (XEXP (op, 1), mode);
+
+ default:
+ return 0;
+ }
+}
+
+/* Emit the code to build the trampoline used to call a nested function.
+
+ 68HC11 68HC12
+
+ ldy #&CXT movw #&CXT,*_.d1
+ sty *_.d1 jmp FNADDR
+ jmp FNADDR
+
+*/
+void
+m68hc11_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
+{
+ const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
+
+ /* Skip the '*'. */
+ if (*static_chain_reg == '*')
+ static_chain_reg++;
+ if (TARGET_M6811)
+ {
+ emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
+ GEN_INT (0x18df));
+ emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
+ gen_rtx_CONST (QImode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ static_chain_reg)));
+ emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
+ GEN_INT (0x7e));
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
+ }
+ else
+ {
+ emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
+ gen_rtx_CONST (HImode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ static_chain_reg)));
+ emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
+ GEN_INT (0x06));
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
+ }
+}
+
+/* Declaration of types. */
+
+/* Handle an "tiny_data" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+m68hc11_handle_page0_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+ tree decl = *node;
+
+ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+ {
+ DECL_SECTION_NAME (decl) = build_string (6, ".page0");
+ }
+ else
+ {
+ warning (OPT_Wattributes, "%qs attribute ignored",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+const struct attribute_spec m68hc11_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* Keep track of the symbol which has a `trap' attribute and which uses
+ the `swi' calling convention. Since there is only one trap, we only
+ record one such symbol. If there are several, a warning is reported. */
+static rtx trap_handler_symbol = 0;
+
+/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
+ arguments as in struct attribute_spec.handler. */
+static tree
+m68hc11_handle_fntype_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_TYPE
+ && TREE_CODE (*node) != METHOD_TYPE
+ && TREE_CODE (*node) != FIELD_DECL
+ && TREE_CODE (*node) != TYPE_DECL)
+ {
+ warning (OPT_Wattributes, "%qs attribute only applies to functions",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+/* Undo the effects of the above. */
+
+static const char *
+m68hc11_strip_name_encoding (const char *str)
+{
+ return str + (*str == '*' || *str == '@' || *str == '&');
+}
+
+static void
+m68hc11_encode_label (tree decl)
+{
+ const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ int len = strlen (str);
+ char *newstr = XALLOCAVEC (char, len + 2);
+
+ newstr[0] = '@';
+ strcpy (&newstr[1], str);
+
+ XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 1);
+}
+
+/* Return 1 if this is a symbol in page0 */
+int
+m68hc11_page0_symbol_p (rtx x)
+{
+ switch (GET_CODE (x))
+ {
+ case SYMBOL_REF:
+ return XSTR (x, 0) != 0 && XSTR (x, 0)[0] == '@';
+
+ case CONST:
+ return m68hc11_page0_symbol_p (XEXP (x, 0));
+
+ case PLUS:
+ if (!m68hc11_page0_symbol_p (XEXP (x, 0)))
+ return 0;
+
+ return GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) < 256
+ && INTVAL (XEXP (x, 1)) >= 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* We want to recognize trap handlers so that we handle calls to traps
+ in a special manner (by issuing the trap). This information is stored
+ in SYMBOL_REF_FLAG. */
+
+static void
+m68hc11_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
+{
+ tree func_attr;
+ int trap_handler;
+ int is_far = 0;
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ if (lookup_attribute ("page0", DECL_ATTRIBUTES (decl)) != 0)
+ m68hc11_encode_label (decl);
+ return;
+ }
+
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ return;
+
+ func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
+
+
+ if (lookup_attribute ("far", func_attr) != NULL_TREE)
+ is_far = 1;
+ else if (lookup_attribute ("near", func_attr) == NULL_TREE)
+ is_far = TARGET_LONG_CALLS != 0;
+
+ trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
+ if (trap_handler && is_far)
+ {
+ warning (OPT_Wattributes, "%<trap%> and %<far%> attributes are "
+ "not compatible, ignoring %<far%>");
+ trap_handler = 0;
+ }
+ if (trap_handler)
+ {
+ if (trap_handler_symbol != 0)
+ warning (OPT_Wattributes, "%<trap%> attribute is already used");
+ else
+ trap_handler_symbol = XEXP (rtl, 0);
+ }
+ SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
+}
+
+static unsigned int
+m68hc11_section_type_flags (tree decl, const char *name, int reloc)
+{
+ unsigned int flags = default_section_type_flags (decl, name, reloc);
+
+ if (strncmp (name, ".eeprom", 7) == 0)
+ {
+ flags |= SECTION_WRITE | SECTION_CODE | SECTION_OVERRIDE;
+ }
+
+ return flags;
+}
+
+int
+m68hc11_is_far_symbol (rtx sym)
+{
+ if (GET_CODE (sym) == MEM)
+ sym = XEXP (sym, 0);
+
+ return SYMBOL_REF_FLAG (sym);
+}
+
+int
+m68hc11_is_trap_symbol (rtx sym)
+{
+ if (GET_CODE (sym) == MEM)
+ sym = XEXP (sym, 0);
+
+ return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
+}
+
+
+/* Argument support functions. */
+
+/* Define the offset between two registers, one to be eliminated, and the
+ other its replacement, at the start of a routine. */
+int
+m68hc11_initial_elimination_offset (int from, int to)
+{
+ int trap_handler;
+ tree func_attr;
+ int size;
+ int regno;
+
+ /* For a trap handler, we must take into account the registers which
+ are pushed on the stack during the trap (except the PC). */
+ func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ current_function_interrupt = lookup_attribute ("interrupt",
+ func_attr) != NULL_TREE;
+ trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
+
+ if (lookup_attribute ("far", func_attr) != 0)
+ current_function_far = 1;
+ else if (lookup_attribute ("near", func_attr) != 0)
+ current_function_far = 0;
+ else
+ current_function_far = (TARGET_LONG_CALLS != 0
+ && !current_function_interrupt
+ && !trap_handler);
+
+ if (trap_handler && from == ARG_POINTER_REGNUM)
+ size = 7;
+
+ /* For a function using 'call/rtc' we must take into account the
+ page register which is pushed in the call. */
+ else if (current_function_far && from == ARG_POINTER_REGNUM)
+ size = 1;
+ else
+ size = 0;
+
+ if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ {
+ /* 2 is for the saved frame.
+ 1 is for the 'sts' correction when creating the frame. */
+ return get_frame_size () + 2 + m68hc11_sp_correction + size;
+ }
+
+ if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ {
+ return m68hc11_sp_correction;
+ }
+
+ /* Push any 2 byte pseudo hard registers that we need to save. */
+ for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
+ {
+ if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ {
+ size += 2;
+ }
+ }
+
+ if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
+ {
+ return get_frame_size () + size;
+ }
+
+ if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
+ {
+ return size;
+ }
+ return 0;
+}
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0. */
+
+void
+m68hc11_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
+{
+ tree ret_type;
+
+ z_replacement_completed = 0;
+ cum->words = 0;
+ cum->nregs = 0;
+
+ /* For a library call, we must find out the type of the return value.
+ When the return value is bigger than 4 bytes, it is returned in
+ memory. In that case, the first argument of the library call is a
+ pointer to the memory location. Because the first argument is passed in
+ register D, we have to identify this, so that the first function
+ parameter is not passed in D either. */
+ if (fntype == 0)
+ {
+ const char *name;
+ size_t len;
+
+ if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
+ return;
+
+ /* If the library ends in 'di' or in 'df', we assume it's
+ returning some DImode or some DFmode which are 64-bit wide. */
+ name = XSTR (libname, 0);
+ len = strlen (name);
+ if (len > 3
+ && ((name[len - 2] == 'd'
+ && (name[len - 1] == 'f' || name[len - 1] == 'i'))
+ || (name[len - 3] == 'd'
+ && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
+ {
+ /* We are in. Mark the first parameter register as already used. */
+ cum->words = 1;
+ cum->nregs = 1;
+ }
+ return;
+ }
+
+ ret_type = TREE_TYPE (fntype);
+
+ if (ret_type && aggregate_value_p (ret_type, fntype))
+ {
+ cum->words = 1;
+ cum->nregs = 1;
+ }
+}
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+
+void
+m68hc11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named ATTRIBUTE_UNUSED)
+{
+ if (mode != BLKmode)
+ {
+ if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
+ {
+ cum->nregs = 2;
+ cum->words = GET_MODE_SIZE (mode);
+ }
+ else
+ {
+ cum->words += GET_MODE_SIZE (mode);
+ if (cum->words <= HARD_REG_SIZE)
+ cum->nregs = 1;
+ }
+ }
+ else
+ {
+ cum->words += int_size_in_bytes (type);
+ }
+ return;
+}
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+
+struct rtx_def *
+m68hc11_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED, int named ATTRIBUTE_UNUSED)
+{
+ if (cum->words != 0)
+ {
+ return NULL_RTX;
+ }
+
+ if (mode != BLKmode)
+ {
+ if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
+ return gen_rtx_REG (mode, HARD_X_REGNUM);
+
+ if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
+ {
+ return NULL_RTX;
+ }
+ return gen_rtx_REG (mode, HARD_D_REGNUM);
+ }
+ return NULL_RTX;
+}
+
+/* If defined, a C expression which determines whether, and in which direction,
+ to pad out an argument with extra space. The value should be of type
+ `enum direction': either `upward' to pad above the argument,
+ `downward' to pad below, or `none' to inhibit padding.
+
+ Structures are stored left shifted in their argument slot. */
+int
+m68hc11_function_arg_padding (enum machine_mode mode, const_tree type)
+{
+ if (type != 0 && AGGREGATE_TYPE_P (type))
+ return upward;
+
+ /* Fall back to the default. */
+ return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
+}
+
+
+/* Function prologue and epilogue. */
+
+/* Emit a move after the reload pass has completed. This is used to
+ emit the prologue and epilogue. */
+static void
+emit_move_after_reload (rtx to, rtx from, rtx scratch)
+{
+ rtx insn;
+
+ if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
+ {
+ insn = emit_move_insn (to, from);
+ }
+ else
+ {
+ emit_move_insn (scratch, from);
+ insn = emit_move_insn (to, scratch);
+ }
+
+ /* Put a REG_INC note to tell the flow analysis that the instruction
+ is necessary. */
+ if (IS_STACK_PUSH (to))
+ {
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
+ XEXP (XEXP (to, 0), 0),
+ REG_NOTES (insn));
+ }
+ else if (IS_STACK_POP (from))
+ {
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
+ XEXP (XEXP (from, 0), 0),
+ REG_NOTES (insn));
+ }
+
+ /* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
+ to think that sp == _.frame and later replace a x = sp with x = _.frame.
+ The problem is that we are lying to gcc and use `txs' for x = sp
+ (which is not really true because txs is really x = sp + 1). */
+ else if (TARGET_M6811 && SP_REG_P (from))
+ {
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
+ from,
+ REG_NOTES (insn));
+ }
+}
+
+int
+m68hc11_total_frame_size (void)
+{
+ int size;
+ int regno;
+
+ size = get_frame_size ();
+ if (current_function_interrupt)
+ {
+ size += 3 * HARD_REG_SIZE;
+ }
+ if (frame_pointer_needed)
+ size += HARD_REG_SIZE;
+
+ for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
+ if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ size += HARD_REG_SIZE;
+
+ return size;
+}
+
+static void
+m68hc11_output_function_epilogue (FILE *out ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ /* We catch the function epilogue generation to have a chance
+ to clear the z_replacement_completed flag. */
+ z_replacement_completed = 0;
+}
+
+void
+expand_prologue (void)
+{
+ tree func_attr;
+ int size;
+ int regno;
+ rtx scratch;
+
+ gcc_assert (reload_completed == 1);
+
+ size = get_frame_size ();
+
+ create_regs_rtx ();
+
+ /* Generate specific prologue for interrupt handlers. */
+ func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ current_function_interrupt = lookup_attribute ("interrupt",
+ func_attr) != NULL_TREE;
+ current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
+ if (lookup_attribute ("far", func_attr) != NULL_TREE)
+ current_function_far = 1;
+ else if (lookup_attribute ("near", func_attr) != NULL_TREE)
+ current_function_far = 0;
+ else
+ current_function_far = (TARGET_LONG_CALLS != 0
+ && !current_function_interrupt
+ && !current_function_trap);
+
+ /* Get the scratch register to build the frame and push registers.
+ If the first argument is a 32-bit quantity, the D+X registers
+ are used. Use Y to compute the frame. Otherwise, X is cheaper.
+ For 68HC12, this scratch register is not used. */
+ if (crtl->args.info.nregs == 2)
+ scratch = iy_reg;
+ else
+ scratch = ix_reg;
+
+ /* Save current stack frame. */
+ if (frame_pointer_needed)
+ emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
+
+ /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
+ Other soft registers in page0 need not to be saved because they
+ will be restored by C functions. For a trap handler, we don't
+ need to preserve these registers because this is a synchronous call. */
+ if (current_function_interrupt)
+ {
+ emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
+ emit_move_after_reload (stack_push_word,
+ gen_rtx_REG (HImode, SOFT_Z_REGNUM), scratch);
+ emit_move_after_reload (stack_push_word,
+ gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
+ scratch);
+ }
+
+ /* Allocate local variables. */
+ if (TARGET_M6812 && (size > 4 || size == 3))
+ {
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (-size)));
+ }
+ else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
+ {
+ rtx insn;
+
+ insn = gen_rtx_PARALLEL
+ (VOIDmode,
+ gen_rtvec (2,
+ gen_rtx_SET (VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx_PLUS (HImode,
+ stack_pointer_rtx,
+ GEN_INT (-size))),
+ gen_rtx_CLOBBER (VOIDmode, scratch)));
+ emit_insn (insn);
+ }
+ else
+ {
+ int i;
+
+ /* Allocate by pushing scratch values. */
+ for (i = 2; i <= size; i += 2)
+ emit_move_after_reload (stack_push_word, ix_reg, 0);
+
+ if (size & 1)
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, constm1_rtx));
+ }
+
+ /* Create the frame pointer. */
+ if (frame_pointer_needed)
+ emit_move_after_reload (hard_frame_pointer_rtx,
+ stack_pointer_rtx, scratch);
+
+ /* Push any 2 byte pseudo hard registers that we need to save. */
+ for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
+ {
+ if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ {
+ emit_move_after_reload (stack_push_word,
+ gen_rtx_REG (HImode, regno), scratch);
+ }
+ }
+}
+
+void
+expand_epilogue (void)
+{
+ int size;
+ register int regno;
+ int return_size;
+ rtx scratch;
+
+ gcc_assert (reload_completed == 1);
+
+ size = get_frame_size ();
+
+ /* If we are returning a value in two registers, we have to preserve the
+ X register and use the Y register to restore the stack and the saved
+ registers. Otherwise, use X because it's faster (and smaller). */
+ if (crtl->return_rtx == 0)
+ return_size = 0;
+ else if (GET_CODE (crtl->return_rtx) == MEM)
+ return_size = HARD_REG_SIZE;
+ else
+ return_size = GET_MODE_SIZE (GET_MODE (crtl->return_rtx));
+
+ if (return_size > HARD_REG_SIZE && return_size <= 2 * HARD_REG_SIZE)
+ scratch = iy_reg;
+ else
+ scratch = ix_reg;
+
+ /* Pop any 2 byte pseudo hard registers that we saved. */
+ for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
+ {
+ if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ {
+ emit_move_after_reload (gen_rtx_REG (HImode, regno),
+ stack_pop_word, scratch);
+ }
+ }
+
+ /* de-allocate auto variables */
+ if (TARGET_M6812 && (size > 4 || size == 3))
+ {
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (size)));
+ }
+ else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
+ {
+ rtx insn;
+
+ insn = gen_rtx_PARALLEL
+ (VOIDmode,
+ gen_rtvec (2,
+ gen_rtx_SET (VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx_PLUS (HImode,
+ stack_pointer_rtx,
+ GEN_INT (size))),
+ gen_rtx_CLOBBER (VOIDmode, scratch)));
+ emit_insn (insn);
+ }
+ else
+ {
+ int i;
+
+ for (i = 2; i <= size; i += 2)
+ emit_move_after_reload (scratch, stack_pop_word, scratch);
+ if (size & 1)
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, const1_rtx));
+ }
+
+ /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
+ if (current_function_interrupt)
+ {
+ emit_move_after_reload (gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
+ stack_pop_word, scratch);
+ emit_move_after_reload (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
+ stack_pop_word, scratch);
+ emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
+ }
+
+ /* Restore previous frame pointer. */
+ if (frame_pointer_needed)
+ emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
+
+ /* If the trap handler returns some value, copy the value
+ in D, X onto the stack so that the rti will pop the return value
+ correctly. */
+ else if (current_function_trap && return_size != 0)
+ {
+ rtx addr_reg = stack_pointer_rtx;
+
+ if (!TARGET_M6812)
+ {
+ emit_move_after_reload (scratch, stack_pointer_rtx, 0);
+ addr_reg = scratch;
+ }
+ emit_move_after_reload (gen_rtx_MEM (HImode,
+ gen_rtx_PLUS (HImode, addr_reg,
+ const1_rtx)), d_reg, 0);
+ if (return_size > HARD_REG_SIZE)
+ emit_move_after_reload (gen_rtx_MEM (HImode,
+ gen_rtx_PLUS (HImode, addr_reg,
+ GEN_INT (3))), ix_reg, 0);
+ }
+
+ emit_jump_insn (gen_return ());
+}
+
+
+/* Low and High part extraction for 68HC11. These routines are
+ similar to gen_lowpart and gen_highpart but they have been
+ fixed to work for constants and 68HC11 specific registers. */
+
+rtx
+m68hc11_gen_lowpart (enum machine_mode mode, rtx x)
+{
+ /* We assume that the low part of an auto-inc mode is the same with
+ the mode changed and that the caller split the larger mode in the
+ correct order. */
+ if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
+ {
+ return gen_rtx_MEM (mode, XEXP (x, 0));
+ }
+
+ /* Note that a CONST_DOUBLE rtx could represent either an integer or a
+ floating-point constant. A CONST_DOUBLE is used whenever the
+ constant requires more than one word in order to be adequately
+ represented. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ long l[2];
+
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE r;
+
+ if (GET_MODE (x) == SFmode)
+ {
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
+ }
+ else
+ {
+ rtx first, second;
+
+ split_double (x, &first, &second);
+ return second;
+ }
+ if (mode == SImode)
+ return GEN_INT (l[0]);
+
+ return gen_int_mode (l[0], HImode);
+ }
+ else
+ {
+ l[0] = CONST_DOUBLE_LOW (x);
+ }
+ switch (mode)
+ {
+ case SImode:
+ return GEN_INT (l[0]);
+ case HImode:
+ gcc_assert (GET_MODE (x) == SFmode);
+ return gen_int_mode (l[0], HImode);
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ if (mode == QImode && D_REG_P (x))
+ return gen_rtx_REG (mode, HARD_B_REGNUM);
+
+ /* gen_lowpart crashes when it is called with a SUBREG. */
+ if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
+ {
+ switch (mode)
+ {
+ case SImode:
+ return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
+ case HImode:
+ return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
+ default:
+ gcc_unreachable ();
+ }
+ }
+ x = gen_lowpart (mode, x);
+
+ /* Return a different rtx to avoid to share it in several insns
+ (when used by a split pattern). Sharing addresses within
+ a MEM breaks the Z register replacement (and reloading). */
+ if (GET_CODE (x) == MEM)
+ x = copy_rtx (x);
+ return x;
+}
+
+rtx
+m68hc11_gen_highpart (enum machine_mode mode, rtx x)
+{
+ /* We assume that the high part of an auto-inc mode is the same with
+ the mode changed and that the caller split the larger mode in the
+ correct order. */
+ if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
+ {
+ return gen_rtx_MEM (mode, XEXP (x, 0));
+ }
+
+ /* Note that a CONST_DOUBLE rtx could represent either an integer or a
+ floating-point constant. A CONST_DOUBLE is used whenever the
+ constant requires more than one word in order to be adequately
+ represented. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ long l[2];
+
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE r;
+
+ if (GET_MODE (x) == SFmode)
+ {
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
+ }
+ else
+ {
+ rtx first, second;
+
+ split_double (x, &first, &second);
+ return first;
+ }
+ if (mode == SImode)
+ return GEN_INT (l[1]);
+
+ return gen_int_mode ((l[1] >> 16), HImode);
+ }
+ else
+ {
+ l[1] = CONST_DOUBLE_HIGH (x);
+ }
+
+ switch (mode)
+ {
+ case SImode:
+ return GEN_INT (l[1]);
+ case HImode:
+ gcc_assert (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
+ return gen_int_mode ((l[0] >> 16), HImode);
+ default:
+ gcc_unreachable ();
+ }
+ }
+ if (GET_CODE (x) == CONST_INT)
+ {
+ HOST_WIDE_INT val = INTVAL (x);
+
+ if (mode == QImode)
+ {
+ return gen_int_mode (val >> 8, QImode);
+ }
+ else if (mode == HImode)
+ {
+ return gen_int_mode (val >> 16, HImode);
+ }
+ else if (mode == SImode)
+ {
+ return gen_int_mode (val >> 32, SImode);
+ }
+ }
+ if (mode == QImode && D_REG_P (x))
+ return gen_rtx_REG (mode, HARD_A_REGNUM);
+
+ /* There is no way in GCC to represent the upper part of a word register.
+ To obtain the 8-bit upper part of a soft register, we change the
+ reg into a mem rtx. This is possible because they are physically
+ located in memory. There is no offset because we are big-endian. */
+ if (mode == QImode && S_REG_P (x))
+ {
+ int pos;
+
+ /* Avoid the '*' for direct addressing mode when this
+ addressing mode is disabled. */
+ pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
+ return gen_rtx_MEM (QImode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ &reg_names[REGNO (x)][pos]));
+ }
+
+ /* gen_highpart crashes when it is called with a SUBREG. */
+ switch (GET_CODE (x))
+ {
+ case SUBREG:
+ return gen_rtx_SUBREG (mode, XEXP (x, 0), XINT (x, 1));
+ case REG:
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+ return gen_rtx_REG (mode, REGNO (x));
+ else
+ return gen_rtx_SUBREG (mode, x, 0);
+ case MEM:
+ x = change_address (x, mode, 0);
+
+ /* Return a different rtx to avoid to share it in several insns
+ (when used by a split pattern). Sharing addresses within
+ a MEM breaks the Z register replacement (and reloading). */
+ if (GET_CODE (x) == MEM)
+ x = copy_rtx (x);
+ return x;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+
+/* Obscure register manipulation. */
+
+/* Finds backward in the instructions to see if register 'reg' is
+ dead. This is used when generating code to see if we can use 'reg'
+ as a scratch register. This allows us to choose a better generation
+ of code when we know that some register dies or can be clobbered. */
+
+int
+dead_register_here (rtx x, rtx reg)
+{
+ rtx x_reg;
+ rtx p;
+
+ if (D_REG_P (reg))
+ x_reg = gen_rtx_REG (SImode, HARD_X_REGNUM);
+ else
+ x_reg = 0;
+
+ for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
+ if (INSN_P (p))
+ {
+ rtx body;
+
+ body = PATTERN (p);
+
+ if (GET_CODE (body) == CALL_INSN)
+ break;
+ if (GET_CODE (body) == JUMP_INSN)
+ break;
+
+ if (GET_CODE (body) == SET)
+ {
+ rtx dst = XEXP (body, 0);
+
+ if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
+ break;
+ if (x_reg && rtx_equal_p (dst, x_reg))
+ break;
+
+ if (find_regno_note (p, REG_DEAD, REGNO (reg)))
+ return 1;
+ }
+ else if (reg_mentioned_p (reg, p)
+ || (x_reg && reg_mentioned_p (x_reg, p)))
+ break;
+ }
+
+ /* Scan forward to see if the register is set in some insns and never
+ used since then. */
+ for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
+ {
+ rtx body;
+
+ if (GET_CODE (p) == CODE_LABEL
+ || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
+ break;
+
+ if (GET_CODE (p) != INSN)
+ continue;
+
+ body = PATTERN (p);
+ if (GET_CODE (body) == SET)
+ {
+ rtx src = XEXP (body, 1);
+ rtx dst = XEXP (body, 0);
+
+ if (GET_CODE (dst) == REG
+ && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
+ return 1;
+ }
+
+ /* Register is used (may be in source or in dest). */
+ if (reg_mentioned_p (reg, p)
+ || (x_reg != 0 && GET_MODE (p) == SImode
+ && reg_mentioned_p (x_reg, p)))
+ break;
+ }
+ return p == 0 ? 1 : 0;
+}
+
+
+/* Code generation operations called from machine description file. */
+
+/* Print the name of register 'regno' in the assembly file. */
+static void
+asm_print_register (FILE *file, int regno)
+{
+ const char *name = reg_names[regno];
+
+ if (TARGET_NO_DIRECT_MODE && name[0] == '*')
+ name++;
+
+ fprintf (file, "%s", name);
+}
+
+/* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand X. X is an RTL
+ expression.
+
+ CODE is a value that can be used to specify one of several ways
+ of printing the operand. It is used when identical operands
+ must be printed differently depending on the context. CODE
+ comes from the `%' specification that was used to request
+ printing of the operand. If the specification was just `%DIGIT'
+ then CODE is 0; if the specification was `%LTR DIGIT' then CODE
+ is the ASCII code for LTR.
+
+ If X is a register, this macro should print the register's name.
+ The names can be found in an array `reg_names' whose type is
+ `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
+
+ When the machine description has a specification `%PUNCT' (a `%'
+ followed by a punctuation character), this macro is called with
+ a null pointer for X and the punctuation character for CODE.
+
+ The M68HC11 specific codes are:
+
+ 'b' for the low part of the operand.
+ 'h' for the high part of the operand
+ The 'b' or 'h' modifiers have no effect if the operand has
+ the QImode and is not a S_REG_P (soft register). If the
+ operand is a hard register, these two modifiers have no effect.
+ 't' generate the temporary scratch register. The operand is
+ ignored.
+ 'T' generate the low-part temporary scratch register. The operand is
+ ignored. */
+
+void
+print_operand (FILE *file, rtx op, int letter)
+{
+ if (letter == 't')
+ {
+ asm_print_register (file, SOFT_TMP_REGNUM);
+ return;
+ }
+ else if (letter == 'T')
+ {
+ asm_print_register (file, SOFT_TMP_REGNUM);
+ fprintf (file, "+1");
+ return;
+ }
+ else if (letter == '#')
+ {
+ asm_fprintf (file, "%I");
+ }
+
+ if (GET_CODE (op) == REG)
+ {
+ if (letter == 'b' && S_REG_P (op))
+ {
+ asm_print_register (file, REGNO (op));
+ fprintf (file, "+1");
+ }
+ else if (letter == 'b' && D_REG_P (op))
+ {
+ asm_print_register (file, HARD_B_REGNUM);
+ }
+ else
+ {
+ asm_print_register (file, REGNO (op));
+ }
+ return;
+ }
+
+ if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
+ {
+ if (letter == 'b')
+ asm_fprintf (file, "%I%%lo(");
+ else
+ asm_fprintf (file, "%I%%hi(");
+
+ output_addr_const (file, op);
+ fprintf (file, ")");
+ return;
+ }
+
+ /* Get the low or high part of the operand when 'b' or 'h' modifiers
+ are specified. If we already have a QImode, there is nothing to do. */
+ if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
+ {
+ if (letter == 'b')
+ {
+ op = m68hc11_gen_lowpart (QImode, op);
+ }
+ else if (letter == 'h')
+ {
+ op = m68hc11_gen_highpart (QImode, op);
+ }
+ }
+
+ if (GET_CODE (op) == MEM)
+ {
+ rtx base = XEXP (op, 0);
+ switch (GET_CODE (base))
+ {
+ case PRE_DEC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ break;
+
+ case POST_DEC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ fprintf (file, "-");
+ break;
+
+ case POST_INC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ fprintf (file, "+");
+ break;
+
+ case PRE_INC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ break;
+
+ case MEM:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "[");
+ print_operand_address (file, XEXP (base, 0));
+ fprintf (file, "]");
+ break;
+
+ default:
+ if (m68hc11_page0_symbol_p (base))
+ fprintf (file, "*");
+
+ output_address (base);
+ break;
+ }
+ }
+ else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
+ {
+ REAL_VALUE_TYPE r;
+ long l;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ asm_fprintf (file, "%I0x%lx", l);
+ }
+ else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
+ {
+ char dstr[30];
+
+ real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
+ sizeof (dstr), 0, 1);
+ asm_fprintf (file, "%I0r%s", dstr);
+ }
+ else
+ {
+ int need_parenthesize = 0;
+
+ if (letter != 'i')
+ asm_fprintf (file, "%I");
+ else
+ need_parenthesize = must_parenthesize (op);
+
+ if (need_parenthesize)
+ fprintf (file, "(");
+
+ output_addr_const (file, op);
+ if (need_parenthesize)
+ fprintf (file, ")");
+ }
+}
+
+/* Returns true if the operand 'op' must be printed with parenthesis
+ around it. This must be done only if there is a symbol whose name
+ is a processor register. */
+static int
+must_parenthesize (rtx op)
+{
+ const char *name;
+
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ name = XSTR (op, 0);
+ /* Avoid a conflict between symbol name and a possible
+ register. */
+ return (strcasecmp (name, "a") == 0
+ || strcasecmp (name, "b") == 0
+ || strcasecmp (name, "d") == 0
+ || strcasecmp (name, "x") == 0
+ || strcasecmp (name, "y") == 0
+ || strcasecmp (name, "ix") == 0
+ || strcasecmp (name, "iy") == 0
+ || strcasecmp (name, "pc") == 0
+ || strcasecmp (name, "sp") == 0
+ || strcasecmp (name, "ccr") == 0) ? 1 : 0;
+
+ case PLUS:
+ case MINUS:
+ return must_parenthesize (XEXP (op, 0))
+ || must_parenthesize (XEXP (op, 1));
+
+ case MEM:
+ case CONST:
+ case ZERO_EXTEND:
+ case SIGN_EXTEND:
+ return must_parenthesize (XEXP (op, 0));
+
+ case CONST_DOUBLE:
+ case CONST_INT:
+ case LABEL_REF:
+ case CODE_LABEL:
+ default:
+ return 0;
+ }
+}
+
+/* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand that is a memory
+ reference whose address is ADDR. ADDR is an RTL expression. */
+
+void
+print_operand_address (FILE *file, rtx addr)
+{
+ rtx base;
+ rtx offset;
+ int need_parenthesis = 0;
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ gcc_assert (REG_P (addr) && REG_OK_FOR_BASE_STRICT_P (addr));
+
+ fprintf (file, "0,");
+ asm_print_register (file, REGNO (addr));
+ break;
+
+ case MEM:
+ base = XEXP (addr, 0);
+ switch (GET_CODE (base))
+ {
+ case PRE_DEC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ break;
+
+ case POST_DEC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ fprintf (file, "-");
+ break;
+
+ case POST_INC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ fprintf (file, "+");
+ break;
+
+ case PRE_INC:
+ gcc_assert (TARGET_M6812);
+ fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
+ asm_print_register (file, REGNO (XEXP (base, 0)));
+ break;
+
+ default:
+ need_parenthesis = must_parenthesize (base);
+ if (need_parenthesis)
+ fprintf (file, "(");
+
+ output_addr_const (file, base);
+ if (need_parenthesis)
+ fprintf (file, ")");
+ break;
+ }
+ break;
+
+ case PLUS:
+ base = XEXP (addr, 0);
+ offset = XEXP (addr, 1);
+ if (!G_REG_P (base) && G_REG_P (offset))
+ {
+ base = XEXP (addr, 1);
+ offset = XEXP (addr, 0);
+ }
+ if (CONSTANT_ADDRESS_P (base))
+ {
+ need_parenthesis = must_parenthesize (addr);
+
+ gcc_assert (CONSTANT_ADDRESS_P (offset));
+ if (need_parenthesis)
+ fprintf (file, "(");
+
+ output_addr_const (file, base);
+ fprintf (file, "+");
+ output_addr_const (file, offset);
+ if (need_parenthesis)
+ fprintf (file, ")");
+ }
+ else
+ {
+ gcc_assert (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base));
+ if (REG_P (offset))
+ {
+ gcc_assert (TARGET_M6812);
+ asm_print_register (file, REGNO (offset));
+ fprintf (file, ",");
+ asm_print_register (file, REGNO (base));
+ }
+ else
+ {
+ need_parenthesis = must_parenthesize (offset);
+ if (need_parenthesis)
+ fprintf (file, "(");
+
+ output_addr_const (file, offset);
+ if (need_parenthesis)
+ fprintf (file, ")");
+ fprintf (file, ",");
+ asm_print_register (file, REGNO (base));
+ }
+ }
+ break;
+
+ default:
+ if (GET_CODE (addr) == CONST_INT
+ && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
+ {
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
+ }
+ else
+ {
+ need_parenthesis = must_parenthesize (addr);
+ if (need_parenthesis)
+ fprintf (file, "(");
+
+ output_addr_const (file, addr);
+ if (need_parenthesis)
+ fprintf (file, ")");
+ }
+ break;
+ }
+}
+
+
+/* Splitting of some instructions. */
+
+static rtx
+m68hc11_expand_compare (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx ret = 0;
+
+ gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT);
+ emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
+ gen_rtx_COMPARE (VOIDmode, op0, op1)));
+ ret = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
+
+ return ret;
+}
+
+rtx
+m68hc11_expand_compare_and_branch (enum rtx_code code, rtx op0, rtx op1,
+ rtx label)
+{
+ rtx tmp;
+
+ switch (GET_MODE (op0))
+ {
+ case QImode:
+ case HImode:
+ tmp = m68hc11_expand_compare (code, op0, op1);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+ return 0;
+#if 0
+
+ /* SCz: from i386.c */
+ case SFmode:
+ case DFmode:
+ /* Don't expand the comparison early, so that we get better code
+ when jump or whoever decides to reverse the comparison. */
+ {
+ rtvec vec;
+ int use_fcomi;
+
+ code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
+ &m68hc11_compare_op1);
+
+ tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
+ m68hc11_compare_op0, m68hc11_compare_op1);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
+
+ use_fcomi = ix86_use_fcomi_compare (code);
+ vec = rtvec_alloc (3 + !use_fcomi);
+ RTVEC_ELT (vec, 0) = tmp;
+ RTVEC_ELT (vec, 1)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
+ RTVEC_ELT (vec, 2)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
+ if (!use_fcomi)
+ RTVEC_ELT (vec, 3)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
+
+ emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
+ return;
+ }
+#endif
+
+ case SImode:
+ /* Expand SImode branch into multiple compare+branch. */
+ {
+ rtx lo[2], hi[2], label2;
+ enum rtx_code code1, code2, code3;
+
+ if (CONSTANT_P (op0) && !CONSTANT_P (op1))
+ {
+ tmp = op0;
+ op0 = op1;
+ op1 = tmp;
+ code = swap_condition (code);
+ }
+ lo[0] = m68hc11_gen_lowpart (HImode, op0);
+ lo[1] = m68hc11_gen_lowpart (HImode, op1);
+ hi[0] = m68hc11_gen_highpart (HImode, op0);
+ hi[1] = m68hc11_gen_highpart (HImode, op1);
+
+ /* Otherwise, if we are doing less-than, op1 is a constant and the
+ low word is zero, then we can just examine the high word. */
+
+ if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
+ && (code == LT || code == LTU))
+ {
+ return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
+ label);
+ }
+
+ /* Otherwise, we need two or three jumps. */
+
+ label2 = gen_label_rtx ();
+
+ code1 = code;
+ code2 = swap_condition (code);
+ code3 = unsigned_condition (code);
+
+ switch (code)
+ {
+ case LT:
+ case GT:
+ case LTU:
+ case GTU:
+ break;
+
+ case LE:
+ code1 = LT;
+ code2 = GT;
+ break;
+ case GE:
+ code1 = GT;
+ code2 = LT;
+ break;
+ case LEU:
+ code1 = LTU;
+ code2 = GTU;
+ break;
+ case GEU:
+ code1 = GTU;
+ code2 = LTU;
+ break;
+
+ case EQ:
+ code1 = UNKNOWN;
+ code2 = NE;
+ break;
+ case NE:
+ code2 = UNKNOWN;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /*
+ * a < b =>
+ * if (hi(a) < hi(b)) goto true;
+ * if (hi(a) > hi(b)) goto false;
+ * if (lo(a) < lo(b)) goto true;
+ * false:
+ */
+ if (code1 != UNKNOWN)
+ m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
+ if (code2 != UNKNOWN)
+ m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
+
+ m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
+
+ if (code2 != UNKNOWN)
+ emit_label (label2);
+ return 0;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+ return 0;
+}
+
+/* Return the increment/decrement mode of a MEM if it is such.
+ Return CONST if it is anything else. */
+static int
+autoinc_mode (rtx x)
+{
+ if (GET_CODE (x) != MEM)
+ return CONST;
+
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == PRE_INC
+ || GET_CODE (x) == PRE_DEC
+ || GET_CODE (x) == POST_INC
+ || GET_CODE (x) == POST_DEC)
+ return GET_CODE (x);
+
+ return CONST;
+}
+
+static int
+m68hc11_make_autoinc_notes (rtx *x, void *data)
+{
+ rtx insn;
+
+ switch (GET_CODE (*x))
+ {
+ case PRE_DEC:
+ case PRE_INC:
+ case POST_DEC:
+ case POST_INC:
+ insn = (rtx) data;
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
+ REG_NOTES (insn));
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Split a DI, SI or HI move into several smaller move operations.
+ The scratch register 'scratch' is used as a temporary to load
+ store intermediate values. It must be a hard register. */
+void
+m68hc11_split_move (rtx to, rtx from, rtx scratch)
+{
+ rtx low_to, low_from;
+ rtx high_to, high_from;
+ rtx insn;
+ enum machine_mode mode;
+ int offset = 0;
+ int autoinc_from = autoinc_mode (from);
+ int autoinc_to = autoinc_mode (to);
+
+ mode = GET_MODE (to);
+
+ /* If the TO and FROM contain autoinc modes that are not compatible
+ together (one pop and the other a push), we must change one to
+ an offsetable operand and generate an appropriate add at the end. */
+ if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
+ {
+ rtx reg;
+ int code;
+
+ /* The source uses an autoinc mode which is not compatible with
+ a split (this would result in a word swap). */
+ if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
+ {
+ code = GET_CODE (XEXP (from, 0));
+ reg = XEXP (XEXP (from, 0), 0);
+ offset = GET_MODE_SIZE (GET_MODE (from));
+ if (code == POST_DEC)
+ offset = -offset;
+
+ if (code == PRE_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+
+ m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
+ if (code == POST_DEC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+ return;
+ }
+
+ /* Likewise for destination. */
+ if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
+ {
+ code = GET_CODE (XEXP (to, 0));
+ reg = XEXP (XEXP (to, 0), 0);
+ offset = GET_MODE_SIZE (GET_MODE (to));
+ if (code == POST_DEC)
+ offset = -offset;
+
+ if (code == PRE_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+
+ m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
+ if (code == POST_DEC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+ return;
+ }
+
+ /* The source and destination auto increment modes must be compatible
+ with each other: same direction. */
+ if ((autoinc_to != autoinc_from
+ && autoinc_to != CONST && autoinc_from != CONST)
+ /* The destination address register must not be used within
+ the source operand because the source address would change
+ while doing the copy. */
+ || (autoinc_to != CONST
+ && reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
+ && !IS_STACK_PUSH (to)))
+ {
+ /* Must change the destination. */
+ code = GET_CODE (XEXP (to, 0));
+ reg = XEXP (XEXP (to, 0), 0);
+ offset = GET_MODE_SIZE (GET_MODE (to));
+ if (code == PRE_DEC || code == POST_DEC)
+ offset = -offset;
+
+ if (code == PRE_DEC || code == PRE_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+ m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
+ if (code == POST_DEC || code == POST_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+
+ return;
+ }
+
+ /* Likewise, the source address register must not be used within
+ the destination operand. */
+ if (autoinc_from != CONST
+ && reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
+ && !IS_STACK_PUSH (to))
+ {
+ /* Must change the source. */
+ code = GET_CODE (XEXP (from, 0));
+ reg = XEXP (XEXP (from, 0), 0);
+ offset = GET_MODE_SIZE (GET_MODE (from));
+ if (code == PRE_DEC || code == POST_DEC)
+ offset = -offset;
+
+ if (code == PRE_DEC || code == PRE_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+ m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
+ if (code == POST_DEC || code == POST_INC)
+ emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
+
+ return;
+ }
+ }
+
+ if (GET_MODE_SIZE (mode) == 8)
+ mode = SImode;
+ else if (GET_MODE_SIZE (mode) == 4)
+ mode = HImode;
+ else
+ mode = QImode;
+
+ if (TARGET_M6812
+ && IS_STACK_PUSH (to)
+ && reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM), from))
+ {
+ if (mode == SImode)
+ {
+ offset = 4;
+ }
+ else if (mode == HImode)
+ {
+ offset = 2;
+ }
+ else
+ offset = 0;
+ }
+
+ low_to = m68hc11_gen_lowpart (mode, to);
+ high_to = m68hc11_gen_highpart (mode, to);
+
+ low_from = m68hc11_gen_lowpart (mode, from);
+ high_from = m68hc11_gen_highpart (mode, from);
+
+ if (offset)
+ {
+ high_from = adjust_address (high_from, mode, offset);
+ low_from = high_from;
+ }
+
+ /* When copying with a POST_INC mode, we must copy the
+ high part and then the low part to guarantee a correct
+ 32/64-bit copy. */
+ if (TARGET_M6812
+ && GET_MODE_SIZE (mode) >= 2
+ && autoinc_from != autoinc_to
+ && (autoinc_from == POST_INC || autoinc_to == POST_INC))
+ {
+ rtx swap;
+
+ swap = low_to;
+ low_to = high_to;
+ high_to = swap;
+
+ swap = low_from;
+ low_from = high_from;
+ high_from = swap;
+ }
+ if (mode == SImode)
+ {
+ m68hc11_split_move (low_to, low_from, scratch);
+ m68hc11_split_move (high_to, high_from, scratch);
+ }
+ else if (H_REG_P (to) || H_REG_P (from)
+ || (low_from == const0_rtx
+ && high_from == const0_rtx
+ && ! push_operand (to, GET_MODE (to))
+ && ! H_REG_P (scratch))
+ || (TARGET_M6812
+ && (!m68hc11_register_indirect_p (from, GET_MODE (from))
+ || m68hc11_small_indexed_indirect_p (from,
+ GET_MODE (from)))
+ && (!m68hc11_register_indirect_p (to, GET_MODE (to))
+ || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
+ {
+ insn = emit_move_insn (low_to, low_from);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+
+ insn = emit_move_insn (high_to, high_from);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+ }
+ else
+ {
+ insn = emit_move_insn (scratch, low_from);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+ insn = emit_move_insn (low_to, scratch);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+
+ insn = emit_move_insn (scratch, high_from);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+ insn = emit_move_insn (high_to, scratch);
+ for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
+ }
+}
+
+static rtx
+simplify_logical (enum machine_mode mode, int code, rtx operand, rtx *result)
+{
+ int val;
+ int mask;
+
+ *result = 0;
+ if (GET_CODE (operand) != CONST_INT)
+ return operand;
+
+ if (mode == HImode)
+ mask = 0x0ffff;
+ else
+ mask = 0x0ff;
+
+ val = INTVAL (operand);
+ switch (code)
+ {
+ case IOR:
+ if ((val & mask) == 0)
+ return 0;
+ if ((val & mask) == mask)
+ *result = constm1_rtx;
+ break;
+
+ case AND:
+ if ((val & mask) == 0)
+ *result = const0_rtx;
+ if ((val & mask) == mask)
+ return 0;
+ break;
+
+ case XOR:
+ if ((val & mask) == 0)
+ return 0;
+ break;
+ }
+ return operand;
+}
+
+static void
+m68hc11_emit_logical (enum machine_mode mode, int code, rtx *operands)
+{
+ rtx result;
+ int need_copy;
+
+ need_copy = (rtx_equal_p (operands[0], operands[1])
+ || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
+
+ operands[1] = simplify_logical (mode, code, operands[1], &result);
+ operands[2] = simplify_logical (mode, code, operands[2], &result);
+
+ if (result && GET_CODE (result) == CONST_INT)
+ {
+ if (!H_REG_P (operands[0]) && operands[3]
+ && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
+ {
+ emit_move_insn (operands[3], result);
+ emit_move_insn (operands[0], operands[3]);
+ }
+ else
+ {
+ emit_move_insn (operands[0], result);
+ }
+ }
+ else if (operands[1] != 0 && operands[2] != 0)
+ {
+ rtx insn;
+
+ if (!H_REG_P (operands[0]) && operands[3])
+ {
+ emit_move_insn (operands[3], operands[1]);
+ emit_insn (gen_rtx_SET (mode,
+ operands[3],
+ gen_rtx_fmt_ee (code, mode,
+ operands[3], operands[2])));
+ insn = emit_move_insn (operands[0], operands[3]);
+ }
+ else
+ {
+ insn = emit_insn (gen_rtx_SET (mode,
+ operands[0],
+ gen_rtx_fmt_ee (code, mode,
+ operands[0],
+ operands[2])));
+ }
+ }
+
+ /* The logical operation is similar to a copy. */
+ else if (need_copy)
+ {
+ rtx src;
+
+ if (GET_CODE (operands[1]) == CONST_INT)
+ src = operands[2];
+ else
+ src = operands[1];
+
+ if (!H_REG_P (operands[0]) && !H_REG_P (src))
+ {
+ emit_move_insn (operands[3], src);
+ emit_move_insn (operands[0], operands[3]);
+ }
+ else
+ {
+ emit_move_insn (operands[0], src);
+ }
+ }
+}
+
+void
+m68hc11_split_logical (enum machine_mode mode, int code, rtx *operands)
+{
+ rtx low[4];
+ rtx high[4];
+
+ low[0] = m68hc11_gen_lowpart (mode, operands[0]);
+ low[1] = m68hc11_gen_lowpart (mode, operands[1]);
+ low[2] = m68hc11_gen_lowpart (mode, operands[2]);
+
+ high[0] = m68hc11_gen_highpart (mode, operands[0]);
+ high[1] = m68hc11_gen_highpart (mode, operands[1]);
+ high[2] = m68hc11_gen_highpart (mode, operands[2]);
+
+ low[3] = operands[3];
+ high[3] = operands[3];
+ if (mode == SImode)
+ {
+ m68hc11_split_logical (HImode, code, low);
+ m68hc11_split_logical (HImode, code, high);
+ return;
+ }
+
+ m68hc11_emit_logical (mode, code, low);
+ m68hc11_emit_logical (mode, code, high);
+}
+
+
+/* Code generation. */
+
+void
+m68hc11_output_swap (rtx insn ATTRIBUTE_UNUSED, rtx operands[])
+{
+ /* We have to be careful with the cc_status. An address register swap
+ is generated for some comparison. The comparison is made with D
+ but the branch really uses the address register. See the split
+ pattern for compare. The xgdx/xgdy preserve the flags but after
+ the exchange, the flags will reflect to the value of X and not D.
+ Tell this by setting the cc_status according to the cc_prev_status. */
+ if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
+ {
+ if (cc_prev_status.value1 != 0
+ && (D_REG_P (cc_prev_status.value1)
+ || X_REG_P (cc_prev_status.value1)))
+ {
+ cc_status = cc_prev_status;
+ if (D_REG_P (cc_status.value1))
+ cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
+ HARD_X_REGNUM);
+ else
+ cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
+ HARD_D_REGNUM);
+ }
+ else
+ CC_STATUS_INIT;
+
+ output_asm_insn ("xgdx", operands);
+ }
+ else
+ {
+ if (cc_prev_status.value1 != 0
+ && (D_REG_P (cc_prev_status.value1)
+ || Y_REG_P (cc_prev_status.value1)))
+ {
+ cc_status = cc_prev_status;
+ if (D_REG_P (cc_status.value1))
+ cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
+ HARD_Y_REGNUM);
+ else
+ cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
+ HARD_D_REGNUM);
+ }
+ else
+ CC_STATUS_INIT;
+
+ output_asm_insn ("xgdy", operands);
+ }
+}
+
+/* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
+ This is used to decide whether a move that set flags should be used
+ instead. */
+int
+next_insn_test_reg (rtx insn, rtx reg)
+{
+ rtx body;
+
+ insn = next_nonnote_insn (insn);
+ if (GET_CODE (insn) != INSN)
+ return 0;
+
+ body = PATTERN (insn);
+ if (sets_cc0_p (body) != 1)
+ return 0;
+
+ if (rtx_equal_p (XEXP (body, 1), reg) == 0)
+ return 0;
+
+ return 1;
+}
+
+/* Generate the code to move a 16-bit operand into another one. */
+
+void
+m68hc11_gen_movhi (rtx insn, rtx *operands)
+{
+ int reg;
+
+ /* Move a register or memory to the same location.
+ This is possible because such insn can appear
+ in a non-optimizing mode. */
+ if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
+ {
+ cc_status = cc_prev_status;
+ return;
+ }
+
+ if (TARGET_M6812)
+ {
+ rtx from = operands[1];
+ rtx to = operands[0];
+
+ if (IS_STACK_PUSH (to) && H_REG_P (from))
+ {
+ cc_status = cc_prev_status;
+ switch (REGNO (from))
+ {
+ case HARD_X_REGNUM:
+ case HARD_Y_REGNUM:
+ case HARD_D_REGNUM:
+ output_asm_insn ("psh%1", operands);
+ break;
+ case HARD_SP_REGNUM:
+ output_asm_insn ("sts\t2,-sp", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return;
+ }
+ if (IS_STACK_POP (from) && H_REG_P (to))
+ {
+ cc_status = cc_prev_status;
+ switch (REGNO (to))
+ {
+ case HARD_X_REGNUM:
+ case HARD_Y_REGNUM:
+ case HARD_D_REGNUM:
+ output_asm_insn ("pul%0", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return;
+ }
+ if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("tfr\t%1,%0", operands);
+ }
+ else if (H_REG_P (operands[0]))
+ {
+ if (SP_REG_P (operands[0]))
+ output_asm_insn ("lds\t%1", operands);
+ else
+ output_asm_insn ("ld%0\t%1", operands);
+ }
+ else if (H_REG_P (operands[1]))
+ {
+ if (SP_REG_P (operands[1]))
+ output_asm_insn ("sts\t%0", operands);
+ else
+ output_asm_insn ("st%1\t%0", operands);
+ }
+
+ /* The 68hc12 does not support (MEM:HI (MEM:HI)) with the movw
+ instruction. We have to use a scratch register as temporary location.
+ Trying to use a specific pattern or constrain failed. */
+ else if (GET_CODE (to) == MEM && GET_CODE (XEXP (to, 0)) == MEM)
+ {
+ rtx ops[4];
+
+ ops[0] = to;
+ ops[2] = from;
+ ops[3] = 0;
+ if (dead_register_here (insn, d_reg))
+ ops[1] = d_reg;
+ else if (dead_register_here (insn, ix_reg))
+ ops[1] = ix_reg;
+ else if (dead_register_here (insn, iy_reg))
+ ops[1] = iy_reg;
+ else
+ {
+ ops[1] = d_reg;
+ ops[3] = d_reg;
+ output_asm_insn ("psh%3", ops);
+ }
+
+ ops[0] = to;
+ ops[2] = from;
+ output_asm_insn ("ld%1\t%2", ops);
+ output_asm_insn ("st%1\t%0", ops);
+ if (ops[3])
+ output_asm_insn ("pul%3", ops);
+ }
+
+ /* Use movw for non-null constants or when we are clearing
+ a volatile memory reference. However, this is possible
+ only if the memory reference has a small offset or is an
+ absolute address. */
+ else if (GET_CODE (from) == CONST_INT
+ && INTVAL (from) == 0
+ && (MEM_VOLATILE_P (to) == 0
+ || m68hc11_small_indexed_indirect_p (to, HImode) == 0))
+ {
+ output_asm_insn ("clr\t%h0", operands);
+ output_asm_insn ("clr\t%b0", operands);
+ }
+ else
+ {
+ if ((m68hc11_register_indirect_p (from, GET_MODE (from))
+ && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
+ || (m68hc11_register_indirect_p (to, GET_MODE (to))
+ && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
+ {
+ rtx ops[3];
+
+ if (operands[2])
+ {
+ ops[0] = operands[2];
+ ops[1] = from;
+ ops[2] = 0;
+ m68hc11_gen_movhi (insn, ops);
+ ops[0] = to;
+ ops[1] = operands[2];
+ m68hc11_gen_movhi (insn, ops);
+ return;
+ }
+ else
+ {
+ /* !!!! SCz wrong here. */
+ fatal_insn ("move insn not handled", insn);
+ }
+ }
+ else
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("movw\t%1,%0", operands);
+ }
+ }
+ return;
+ }
+
+ if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
+ {
+ cc_status = cc_prev_status;
+ switch (REGNO (operands[0]))
+ {
+ case HARD_X_REGNUM:
+ case HARD_Y_REGNUM:
+ output_asm_insn ("pul%0", operands);
+ break;
+ case HARD_D_REGNUM:
+ output_asm_insn ("pula", operands);
+ output_asm_insn ("pulb", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return;
+ }
+ /* Some moves to a hard register are special. Not all of them
+ are really supported and we have to use a temporary
+ location to provide them (either the stack of a temp var). */
+ if (H_REG_P (operands[0]))
+ {
+ switch (REGNO (operands[0]))
+ {
+ case HARD_D_REGNUM:
+ if (X_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else if (next_insn_test_reg (insn, operands[0]))
+ {
+ output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
+ }
+ else
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
+ }
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else
+ {
+ /* %t means *ZTMP scratch register. */
+ output_asm_insn ("sty\t%t1", operands);
+ output_asm_insn ("ldd\t%t1", operands);
+ }
+ }
+ else if (SP_REG_P (operands[1]))
+ {
+ CC_STATUS_INIT;
+ if (ix_reg == 0)
+ create_regs_rtx ();
+ if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
+ output_asm_insn ("xgdx", operands);
+ output_asm_insn ("tsx", operands);
+ output_asm_insn ("xgdx", operands);
+ }
+ else if (IS_STACK_POP (operands[1]))
+ {
+ output_asm_insn ("pula\n\tpulb", operands);
+ }
+ else if (GET_CODE (operands[1]) == CONST_INT
+ && INTVAL (operands[1]) == 0)
+ {
+ output_asm_insn ("clra\n\tclrb", operands);
+ }
+ else
+ {
+ output_asm_insn ("ldd\t%1", operands);
+ }
+ break;
+
+ case HARD_X_REGNUM:
+ if (D_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else if (next_insn_test_reg (insn, operands[0]))
+ {
+ output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
+ }
+ else
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("pshb", operands);
+ output_asm_insn ("psha", operands);
+ output_asm_insn ("pulx", operands);
+ }
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ /* When both D and Y are dead, use the sequence xgdy, xgdx
+ to move Y into X. The D and Y registers are modified. */
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
+ && dead_register_here (insn, d_reg))
+ {
+ output_asm_insn ("xgdy", operands);
+ output_asm_insn ("xgdx", operands);
+ CC_STATUS_INIT;
+ }
+ else if (!optimize_size)
+ {
+ output_asm_insn ("sty\t%t1", operands);
+ output_asm_insn ("ldx\t%t1", operands);
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ output_asm_insn ("pshy", operands);
+ output_asm_insn ("pulx", operands);
+ }
+ }
+ else if (SP_REG_P (operands[1]))
+ {
+ /* tsx, tsy preserve the flags */
+ cc_status = cc_prev_status;
+ output_asm_insn ("tsx", operands);
+ }
+ else
+ {
+ output_asm_insn ("ldx\t%1", operands);
+ }
+ break;
+
+ case HARD_Y_REGNUM:
+ if (D_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else
+ {
+ output_asm_insn ("std\t%t1", operands);
+ output_asm_insn ("ldy\t%t1", operands);
+ }
+ }
+ else if (X_REG_P (operands[1]))
+ {
+ /* When both D and X are dead, use the sequence xgdx, xgdy
+ to move X into Y. The D and X registers are modified. */
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
+ && dead_register_here (insn, d_reg))
+ {
+ output_asm_insn ("xgdx", operands);
+ output_asm_insn ("xgdy", operands);
+ CC_STATUS_INIT;
+ }
+ else if (!optimize_size)
+ {
+ output_asm_insn ("stx\t%t1", operands);
+ output_asm_insn ("ldy\t%t1", operands);
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ output_asm_insn ("pshx", operands);
+ output_asm_insn ("puly", operands);
+ }
+ }
+ else if (SP_REG_P (operands[1]))
+ {
+ /* tsx, tsy preserve the flags */
+ cc_status = cc_prev_status;
+ output_asm_insn ("tsy", operands);
+ }
+ else
+ {
+ output_asm_insn ("ldy\t%1", operands);
+ }
+ break;
+
+ case HARD_SP_REGNUM:
+ if (D_REG_P (operands[1]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("xgdx", operands);
+ output_asm_insn ("txs", operands);
+ output_asm_insn ("xgdx", operands);
+ }
+ else if (X_REG_P (operands[1]))
+ {
+ /* tys, txs preserve the flags */
+ cc_status = cc_prev_status;
+ output_asm_insn ("txs", operands);
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ /* tys, txs preserve the flags */
+ cc_status = cc_prev_status;
+ output_asm_insn ("tys", operands);
+ }
+ else
+ {
+ /* lds sets the flags but the des does not. */
+ CC_STATUS_INIT;
+ output_asm_insn ("lds\t%1", operands);
+ output_asm_insn ("des", operands);
+ }
+ break;
+
+ default:
+ fatal_insn ("invalid register in the move instruction", insn);
+ break;
+ }
+ return;
+ }
+ if (SP_REG_P (operands[1]) && REG_P (operands[0])
+ && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
+ {
+ output_asm_insn ("sts\t%0", operands);
+ return;
+ }
+
+ if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
+ {
+ cc_status = cc_prev_status;
+ switch (REGNO (operands[1]))
+ {
+ case HARD_X_REGNUM:
+ case HARD_Y_REGNUM:
+ output_asm_insn ("psh%1", operands);
+ break;
+ case HARD_D_REGNUM:
+ output_asm_insn ("pshb", operands);
+ output_asm_insn ("psha", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return;
+ }
+
+ /* Operand 1 must be a hard register. */
+ if (!H_REG_P (operands[1]))
+ {
+ fatal_insn ("invalid operand in the instruction", insn);
+ }
+
+ reg = REGNO (operands[1]);
+ switch (reg)
+ {
+ case HARD_D_REGNUM:
+ output_asm_insn ("std\t%0", operands);
+ break;
+
+ case HARD_X_REGNUM:
+ output_asm_insn ("stx\t%0", operands);
+ break;
+
+ case HARD_Y_REGNUM:
+ output_asm_insn ("sty\t%0", operands);
+ break;
+
+ case HARD_SP_REGNUM:
+ if (ix_reg == 0)
+ create_regs_rtx ();
+
+ if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
+ {
+ output_asm_insn ("pshx", operands);
+ output_asm_insn ("tsx", operands);
+ output_asm_insn ("inx", operands);
+ output_asm_insn ("inx", operands);
+ output_asm_insn ("stx\t%0", operands);
+ output_asm_insn ("pulx", operands);
+ }
+
+ else if (reg_mentioned_p (ix_reg, operands[0]))
+ {
+ output_asm_insn ("sty\t%t0", operands);
+ output_asm_insn ("tsy", operands);
+ output_asm_insn ("sty\t%0", operands);
+ output_asm_insn ("ldy\t%t0", operands);
+ }
+ else
+ {
+ output_asm_insn ("stx\t%t0", operands);
+ output_asm_insn ("tsx", operands);
+ output_asm_insn ("stx\t%0", operands);
+ output_asm_insn ("ldx\t%t0", operands);
+ }
+ CC_STATUS_INIT;
+ break;
+
+ default:
+ fatal_insn ("invalid register in the move instruction", insn);
+ break;
+ }
+}
+
+void
+m68hc11_gen_movqi (rtx insn, rtx *operands)
+{
+ /* Move a register or memory to the same location.
+ This is possible because such insn can appear
+ in a non-optimizing mode. */
+ if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
+ {
+ cc_status = cc_prev_status;
+ return;
+ }
+
+ if (TARGET_M6812)
+ {
+
+ if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("tfr\t%1,%0", operands);
+ }
+ else if (H_REG_P (operands[0]))
+ {
+ if (IS_STACK_POP (operands[1]))
+ output_asm_insn ("pul%b0", operands);
+ else if (Q_REG_P (operands[0]))
+ output_asm_insn ("lda%0\t%b1", operands);
+ else if (D_REG_P (operands[0]))
+ output_asm_insn ("ldab\t%b1", operands);
+ else
+ goto m6811_move;
+ }
+ else if (H_REG_P (operands[1]))
+ {
+ if (Q_REG_P (operands[1]))
+ output_asm_insn ("sta%1\t%b0", operands);
+ else if (D_REG_P (operands[1]))
+ output_asm_insn ("stab\t%b0", operands);
+ else
+ goto m6811_move;
+ }
+ else
+ {
+ rtx from = operands[1];
+ rtx to = operands[0];
+
+ if ((m68hc11_register_indirect_p (from, GET_MODE (from))
+ && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
+ || (m68hc11_register_indirect_p (to, GET_MODE (to))
+ && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
+ {
+ rtx ops[3];
+
+ if (operands[2])
+ {
+ ops[0] = operands[2];
+ ops[1] = from;
+ ops[2] = 0;
+ m68hc11_gen_movqi (insn, ops);
+ ops[0] = to;
+ ops[1] = operands[2];
+ m68hc11_gen_movqi (insn, ops);
+ }
+ else
+ {
+ /* !!!! SCz wrong here. */
+ fatal_insn ("move insn not handled", insn);
+ }
+ }
+ else
+ {
+ if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
+ {
+ output_asm_insn ("clr\t%b0", operands);
+ }
+ else
+ {
+ m68hc11_notice_keep_cc (operands[0]);
+ output_asm_insn ("movb\t%b1,%b0", operands);
+ }
+ }
+ }
+ return;
+ }
+
+ m6811_move:
+ if (H_REG_P (operands[0]))
+ {
+ switch (REGNO (operands[0]))
+ {
+ case HARD_B_REGNUM:
+ case HARD_D_REGNUM:
+ if (X_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else
+ {
+ output_asm_insn ("stx\t%t1", operands);
+ output_asm_insn ("ldab\t%T0", operands);
+ }
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else
+ {
+ output_asm_insn ("sty\t%t1", operands);
+ output_asm_insn ("ldab\t%T0", operands);
+ }
+ }
+ else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
+ && !DA_REG_P (operands[1]))
+ {
+ output_asm_insn ("ldab\t%b1", operands);
+ }
+ else if (DA_REG_P (operands[1]))
+ {
+ output_asm_insn ("tab", operands);
+ }
+ else
+ {
+ cc_status = cc_prev_status;
+ return;
+ }
+ break;
+
+ case HARD_A_REGNUM:
+ if (X_REG_P (operands[1]))
+ {
+ output_asm_insn ("stx\t%t1", operands);
+ output_asm_insn ("ldaa\t%T0", operands);
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ output_asm_insn ("sty\t%t1", operands);
+ output_asm_insn ("ldaa\t%T0", operands);
+ }
+ else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
+ && !DA_REG_P (operands[1]))
+ {
+ output_asm_insn ("ldaa\t%b1", operands);
+ }
+ else if (!DA_REG_P (operands[1]))
+ {
+ output_asm_insn ("tba", operands);
+ }
+ else
+ {
+ cc_status = cc_prev_status;
+ }
+ break;
+
+ case HARD_X_REGNUM:
+ if (D_REG_P (operands[1]))
+ {
+ if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+ {
+ m68hc11_output_swap (insn, operands);
+ }
+ else
+ {
+ output_asm_insn ("stab\t%T1", operands);
+ output_asm_insn ("ldx\t%t1", operands);
+ }
+ CC_STATUS_INIT;
+ }
+ else if (Y_REG_P (operands[1]))
+ {
+ output_asm_insn ("sty\t%t0", operands);
+ output_asm_insn ("ldx\t%t0", operands);
+ }
+ else if (GET_CODE (operands[1]) == CONST_INT)
+ {
+ output_asm_insn ("ldx\t%1", operands);
+ }
+ else if (dead_register_here (insn, d_reg))
+ {
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("xgdx", operands);
+ }
+ else if (!reg_mentioned_p (operands[0], operands[1]))
+ {
+ output_asm_insn ("xgdx", operands);
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("xgdx", operands);
+ }
+ else
+ {
+ output_asm_insn ("pshb", operands);
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("stab\t%T1", operands);
+ output_asm_insn ("ldx\t%t1", operands);
+ output_asm_insn ("pulb", operands);
+ CC_STATUS_INIT;
+ }
+ break;
+
+ case HARD_Y_REGNUM:
+ if (D_REG_P (operands[1]))
+ {
+ output_asm_insn ("stab\t%T1", operands);
+ output_asm_insn ("ldy\t%t1", operands);
+ CC_STATUS_INIT;
+ }
+ else if (X_REG_P (operands[1]))
+ {
+ output_asm_insn ("stx\t%t1", operands);
+ output_asm_insn ("ldy\t%t1", operands);
+ CC_STATUS_INIT;
+ }
+ else if (GET_CODE (operands[1]) == CONST_INT)
+ {
+ output_asm_insn ("ldy\t%1", operands);
+ }
+ else if (dead_register_here (insn, d_reg))
+ {
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("xgdy", operands);
+ }
+ else if (!reg_mentioned_p (operands[0], operands[1]))
+ {
+ output_asm_insn ("xgdy", operands);
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("xgdy", operands);
+ }
+ else
+ {
+ output_asm_insn ("pshb", operands);
+ output_asm_insn ("ldab\t%b1", operands);
+ output_asm_insn ("stab\t%T1", operands);
+ output_asm_insn ("ldy\t%t1", operands);
+ output_asm_insn ("pulb", operands);
+ CC_STATUS_INIT;
+ }
+ break;
+
+ default:
+ fatal_insn ("invalid register in the instruction", insn);
+ break;
+ }
+ }
+ else if (H_REG_P (operands[1]))
+ {
+ switch (REGNO (operands[1]))
+ {
+ case HARD_D_REGNUM:
+ case HARD_B_REGNUM:
+ output_asm_insn ("stab\t%b0", operands);
+ break;
+
+ case HARD_A_REGNUM:
+ output_asm_insn ("staa\t%b0", operands);
+ break;
+
+ case HARD_X_REGNUM:
+ output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
+ break;
+
+ case HARD_Y_REGNUM:
+ output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
+ break;
+
+ default:
+ fatal_insn ("invalid register in the move instruction", insn);
+ break;
+ }
+ return;
+ }
+ else
+ {
+ fatal_insn ("operand 1 must be a hard register", insn);
+ }
+}
+
+/* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
+ The source and destination must be D or A and the shift must
+ be a constant. */
+void
+m68hc11_gen_rotate (enum rtx_code code, rtx insn, rtx operands[])
+{
+ int val;
+
+ if (GET_CODE (operands[2]) != CONST_INT
+ || (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
+ fatal_insn ("invalid rotate insn", insn);
+
+ val = INTVAL (operands[2]);
+ if (code == ROTATERT)
+ val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
+
+ if (GET_MODE (operands[0]) != QImode)
+ CC_STATUS_INIT;
+
+ /* Rotate by 8-bits if the shift is within [5..11]. */
+ if (val >= 5 && val <= 11)
+ {
+ if (TARGET_M6812)
+ output_asm_insn ("exg\ta,b", operands);
+ else
+ {
+ output_asm_insn ("psha", operands);
+ output_asm_insn ("tba", operands);
+ output_asm_insn ("pulb", operands);
+ }
+ val -= 8;
+ }
+
+ /* If the shift is big, invert the rotation. */
+ else if (val >= 12)
+ {
+ val = val - 16;
+ }
+
+ if (val > 0)
+ {
+ while (--val >= 0)
+ {
+ /* Set the carry to bit-15, but don't change D yet. */
+ if (GET_MODE (operands[0]) != QImode)
+ {
+ output_asm_insn ("asra", operands);
+ output_asm_insn ("rola", operands);
+ }
+
+ /* Rotate B first to move the carry to bit-0. */
+ if (D_REG_P (operands[0]))
+ output_asm_insn ("rolb", operands);
+
+ if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
+ output_asm_insn ("rola", operands);
+ }
+ }
+ else
+ {
+ while (++val <= 0)
+ {
+ /* Set the carry to bit-8 of D. */
+ if (GET_MODE (operands[0]) != QImode)
+ output_asm_insn ("tap", operands);
+
+ /* Rotate B first to move the carry to bit-7. */
+ if (D_REG_P (operands[0]))
+ output_asm_insn ("rorb", operands);
+
+ if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
+ output_asm_insn ("rora", operands);
+ }
+ }
+}
+
+
+
+/* Store in cc_status the expressions that the condition codes will
+ describe after execution of an instruction whose pattern is EXP.
+ Do not alter them if the instruction would not alter the cc's. */
+
+void
+m68hc11_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
+{
+ /* recognize SET insn's. */
+ if (GET_CODE (exp) == SET)
+ {
+ /* Jumps do not alter the cc's. */
+ if (SET_DEST (exp) == pc_rtx)
+ ;
+
+ /* NOTE: most instructions don't affect the carry bit, but the
+ bhi/bls/bhs/blo instructions use it. This isn't mentioned in
+ the conditions.h header. */
+
+ /* Function calls clobber the cc's. */
+ else if (GET_CODE (SET_SRC (exp)) == CALL)
+ {
+ CC_STATUS_INIT;
+ }
+
+ /* Tests and compares set the cc's in predictable ways. */
+ else if (SET_DEST (exp) == cc0_rtx)
+ {
+ cc_status.flags = 0;
+ cc_status.value1 = XEXP (exp, 0);
+ cc_status.value2 = XEXP (exp, 1);
+ }
+ else
+ {
+ /* All other instructions affect the condition codes. */
+ cc_status.flags = 0;
+ cc_status.value1 = XEXP (exp, 0);
+ cc_status.value2 = XEXP (exp, 1);
+ }
+ }
+ else
+ {
+ /* Default action if we haven't recognized something
+ and returned earlier. */
+ CC_STATUS_INIT;
+ }
+
+ if (cc_status.value2 != 0)
+ switch (GET_CODE (cc_status.value2))
+ {
+ /* These logical operations can generate several insns.
+ The flags are setup according to what is generated. */
+ case IOR:
+ case XOR:
+ case AND:
+ break;
+
+ /* The (not ...) generates several 'com' instructions for
+ non QImode. We have to invalidate the flags. */
+ case NOT:
+ if (GET_MODE (cc_status.value2) != QImode)
+ CC_STATUS_INIT;
+ break;
+
+ case PLUS:
+ case MINUS:
+ case MULT:
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ case NEG:
+ if (GET_MODE (cc_status.value2) != VOIDmode)
+ cc_status.flags |= CC_NO_OVERFLOW;
+ break;
+
+ /* The asl sets the overflow bit in such a way that this
+ makes the flags unusable for a next compare insn. */
+ case ASHIFT:
+ case ROTATE:
+ case ROTATERT:
+ if (GET_MODE (cc_status.value2) != VOIDmode)
+ cc_status.flags |= CC_NO_OVERFLOW;
+ break;
+
+ /* A load/store instruction does not affect the carry. */
+ case MEM:
+ case SYMBOL_REF:
+ case REG:
+ case CONST_INT:
+ cc_status.flags |= CC_NO_OVERFLOW;
+ break;
+
+ default:
+ break;
+ }
+ if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
+ && cc_status.value2
+ && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
+ cc_status.value2 = 0;
+
+ else if (cc_status.value1 && side_effects_p (cc_status.value1))
+ cc_status.value1 = 0;
+
+ else if (cc_status.value2 && side_effects_p (cc_status.value2))
+ cc_status.value2 = 0;
+}
+
+/* The current instruction does not affect the flags but changes
+ the register 'reg'. See if the previous flags can be kept for the
+ next instruction to avoid a comparison. */
+void
+m68hc11_notice_keep_cc (rtx reg)
+{
+ if (reg == 0
+ || cc_prev_status.value1 == 0
+ || rtx_equal_p (reg, cc_prev_status.value1)
+ || (cc_prev_status.value2
+ && reg_mentioned_p (reg, cc_prev_status.value2)))
+ CC_STATUS_INIT;
+ else
+ cc_status = cc_prev_status;
+}
+
+
+
+/* Machine Specific Reorg. */
+
+/* Z register replacement:
+
+ GCC treats the Z register as an index base address register like
+ X or Y. In general, it uses it during reload to compute the address
+ of some operand. This helps the reload pass to avoid to fall into the
+ register spill failure.
+
+ The Z register is in the A_REGS class. In the machine description,
+ the 'A' constraint matches it. The 'x' or 'y' constraints do not.
+
+ It can appear everywhere an X or Y register can appear, except for
+ some templates in the clobber section (when a clobber of X or Y is asked).
+ For a given instruction, the template must ensure that no more than
+ 2 'A' registers are used. Otherwise, the register replacement is not
+ possible.
+
+ To replace the Z register, the algorithm is not terrific:
+ 1. Insns that do not use the Z register are not changed
+ 2. When a Z register is used, we scan forward the insns to see
+ a potential register to use: either X or Y and sometimes D.
+ We stop when a call, a label or a branch is seen, or when we
+ detect that both X and Y are used (probably at different times, but it does
+ not matter).
+ 3. The register that will be used for the replacement of Z is saved
+ in a .page0 register or on the stack. If the first instruction that
+ used Z, uses Z as an input, the value is loaded from another .page0
+ register. The replacement register is pushed on the stack in the
+ rare cases where a compare insn uses Z and we couldn't find if X/Y
+ are dead.
+ 4. The Z register is replaced in all instructions until we reach
+ the end of the Z-block, as detected by step 2.
+ 5. If we detect that Z is still alive, its value is saved.
+ If the replacement register is alive, its old value is loaded.
+
+ The Z register can be disabled with -ffixed-z.
+*/
+
+struct replace_info
+{
+ rtx first;
+ rtx replace_reg;
+ int need_save_z;
+ int must_load_z;
+ int must_save_reg;
+ int must_restore_reg;
+ rtx last;
+ int regno;
+ int x_used;
+ int y_used;
+ int can_use_d;
+ int found_call;
+ int z_died;
+ int z_set_count;
+ rtx z_value;
+ int must_push_reg;
+ int save_before_last;
+ int z_loaded_with_sp;
+};
+
+static int m68hc11_check_z_replacement (rtx, struct replace_info *);
+static void m68hc11_find_z_replacement (rtx, struct replace_info *);
+static void m68hc11_z_replacement (rtx);
+static void m68hc11_reassign_regs (rtx);
+
+int z_replacement_completed = 0;
+
+/* Analyze the insn to find out which replacement register to use and
+ the boundaries of the replacement.
+ Returns 0 if we reached the last insn to be replaced, 1 if we can
+ continue replacement in next insns. */
+
+static int
+m68hc11_check_z_replacement (rtx insn, struct replace_info *info)
+{
+ int this_insn_uses_ix;
+ int this_insn_uses_iy;
+ int this_insn_uses_z;
+ int this_insn_uses_z_in_dst;
+ int this_insn_uses_d;
+ rtx body;
+ int z_dies_here;
+
+ /* A call is said to clobber the Z register, we don't need
+ to save the value of Z. We also don't need to restore
+ the replacement register (unless it is used by the call). */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ body = PATTERN (insn);
+
+ info->can_use_d = 0;
+
+ /* If the call is an indirect call with Z, we have to use the
+ Y register because X can be used as an input (D+X).
+ We also must not save Z nor restore Y. */
+ if (reg_mentioned_p (z_reg, body))
+ {
+ insn = NEXT_INSN (insn);
+ info->x_used = 1;
+ info->y_used = 0;
+ info->found_call = 1;
+ info->must_restore_reg = 0;
+ info->last = NEXT_INSN (insn);
+ }
+ info->need_save_z = 0;
+ return 0;
+ }
+ if (GET_CODE (insn) == CODE_LABEL
+ || GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
+ return 0;
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (reg_mentioned_p (z_reg, insn) == 0)
+ return 0;
+
+ info->can_use_d = 0;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ info->need_save_z = 0;
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+ if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
+ {
+ return 1;
+ }
+
+ /* Z register dies here. */
+ z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
+
+ body = PATTERN (insn);
+ if (GET_CODE (body) == SET)
+ {
+ rtx src = XEXP (body, 1);
+ rtx dst = XEXP (body, 0);
+
+ /* Condition code is set here. We have to restore the X/Y and
+ save into Z before any test/compare insn because once we save/restore
+ we can change the condition codes. When the compare insn uses Z and
+ we can't use X/Y, the comparison is made with the *ZREG soft register
+ (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
+ if (dst == cc0_rtx)
+ {
+ if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
+ || (GET_CODE (src) == COMPARE &&
+ ((rtx_equal_p (XEXP (src, 0), z_reg)
+ && H_REG_P (XEXP (src, 1)))
+ || (rtx_equal_p (XEXP (src, 1), z_reg)
+ && H_REG_P (XEXP (src, 0))))))
+ {
+ if (insn == info->first)
+ {
+ info->must_load_z = 0;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ info->need_save_z = 0;
+ info->found_call = 1;
+ info->regno = SOFT_Z_REGNUM;
+ info->last = NEXT_INSN (insn);
+ }
+ return 0;
+ }
+ if (reg_mentioned_p (z_reg, src) == 0)
+ {
+ info->can_use_d = 0;
+ return 0;
+ }
+
+ if (insn != info->first)
+ return 0;
+
+ /* Compare insn which uses Z. We have to save/restore the X/Y
+ register without modifying the condition codes. For this
+ we have to use a push/pop insn. */
+ info->must_push_reg = 1;
+ info->last = insn;
+ }
+
+ /* Z reg is set to something new. We don't need to load it. */
+ if (Z_REG_P (dst))
+ {
+ if (!reg_mentioned_p (z_reg, src))
+ {
+ /* Z reg is used before being set. Treat this as
+ a new sequence of Z register replacement. */
+ if (insn != info->first)
+ {
+ return 0;
+ }
+ info->must_load_z = 0;
+ }
+ info->z_set_count++;
+ info->z_value = src;
+ if (SP_REG_P (src))
+ info->z_loaded_with_sp = 1;
+ }
+ else if (reg_mentioned_p (z_reg, dst))
+ info->can_use_d = 0;
+
+ this_insn_uses_d = reg_mentioned_p (d_reg, src)
+ | reg_mentioned_p (d_reg, dst);
+ this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
+ | reg_mentioned_p (ix_reg, dst);
+ this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
+ | reg_mentioned_p (iy_reg, dst);
+ this_insn_uses_z = reg_mentioned_p (z_reg, src);
+
+ /* If z is used as an address operand (like (MEM (reg z))),
+ we can't replace it with d. */
+ if (this_insn_uses_z && !Z_REG_P (src)
+ && !(m68hc11_arith_operator (src, GET_MODE (src))
+ && Z_REG_P (XEXP (src, 0))
+ && !reg_mentioned_p (z_reg, XEXP (src, 1))
+ && insn == info->first
+ && dead_register_here (insn, d_reg)))
+ info->can_use_d = 0;
+
+ this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
+ if (TARGET_M6812 && !z_dies_here
+ && ((this_insn_uses_z && side_effects_p (src))
+ || (this_insn_uses_z_in_dst && side_effects_p (dst))))
+ {
+ info->need_save_z = 1;
+ info->z_set_count++;
+ }
+ this_insn_uses_z |= this_insn_uses_z_in_dst;
+
+ if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
+ {
+ fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
+ }
+
+ if (this_insn_uses_d)
+ info->can_use_d = 0;
+
+ /* IX and IY are used at the same time, we have to restore
+ the value of the scratch register before this insn. */
+ if (this_insn_uses_ix && this_insn_uses_iy)
+ {
+ return 0;
+ }
+
+ if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
+ info->can_use_d = 0;
+
+ if (info->x_used == 0 && this_insn_uses_ix)
+ {
+ if (info->y_used)
+ {
+ /* We have a (set (REG:HI X) (REG:HI Z)).
+ Since we use Z as the replacement register, this insn
+ is no longer necessary. We turn it into a note. We must
+ not reload the old value of X. */
+ if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
+ {
+ if (z_dies_here)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ }
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ info->found_call = 1;
+ info->can_use_d = 0;
+ SET_INSN_DELETED (insn);
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+
+ if (X_REG_P (dst)
+ && (rtx_equal_p (src, z_reg)
+ || (z_dies_here && !reg_mentioned_p (ix_reg, src))))
+ {
+ if (z_dies_here)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ }
+ info->last = NEXT_INSN (insn);
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
+ && !reg_mentioned_p (ix_reg, src))
+ {
+ if (z_dies_here)
+ {
+ info->z_died = 1;
+ info->need_save_z = 0;
+ }
+ else if (TARGET_M6812 && side_effects_p (src))
+ {
+ info->last = 0;
+ info->must_restore_reg = 0;
+ return 0;
+ }
+ else
+ {
+ info->save_before_last = 1;
+ }
+ info->must_restore_reg = 0;
+ info->last = NEXT_INSN (insn);
+ }
+ else if (info->can_use_d)
+ {
+ info->last = NEXT_INSN (insn);
+ info->x_used = 1;
+ }
+ return 0;
+ }
+ info->x_used = 1;
+ if (z_dies_here && !reg_mentioned_p (ix_reg, src)
+ && GET_CODE (dst) == REG && REGNO (dst) == HARD_X_REGNUM)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ info->last = NEXT_INSN (insn);
+ info->regno = HARD_X_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ return 0;
+ }
+ if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, ix_reg))
+ {
+ info->regno = HARD_X_REGNUM;
+ info->must_restore_reg = 0;
+ info->must_save_reg = 0;
+ return 0;
+ }
+ }
+ if (info->y_used == 0 && this_insn_uses_iy)
+ {
+ if (info->x_used)
+ {
+ if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
+ {
+ if (z_dies_here)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ }
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ info->found_call = 1;
+ info->can_use_d = 0;
+ SET_INSN_DELETED (insn);
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+
+ if (Y_REG_P (dst)
+ && (rtx_equal_p (src, z_reg)
+ || (z_dies_here && !reg_mentioned_p (iy_reg, src))))
+ {
+ if (z_dies_here)
+ {
+ info->z_died = 1;
+ info->need_save_z = 0;
+ }
+ info->last = NEXT_INSN (insn);
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
+ && !reg_mentioned_p (iy_reg, src))
+ {
+ if (z_dies_here)
+ {
+ info->z_died = 1;
+ info->need_save_z = 0;
+ }
+ else if (TARGET_M6812 && side_effects_p (src))
+ {
+ info->last = 0;
+ info->must_restore_reg = 0;
+ return 0;
+ }
+ else
+ {
+ info->save_before_last = 1;
+ }
+ info->must_restore_reg = 0;
+ info->last = NEXT_INSN (insn);
+ }
+ else if (info->can_use_d)
+ {
+ info->last = NEXT_INSN (insn);
+ info->y_used = 1;
+ }
+
+ return 0;
+ }
+ info->y_used = 1;
+ if (z_dies_here && !reg_mentioned_p (iy_reg, src)
+ && GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ info->last = NEXT_INSN (insn);
+ info->regno = HARD_Y_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ return 0;
+ }
+ if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
+ {
+ info->regno = HARD_Y_REGNUM;
+ info->must_restore_reg = 0;
+ info->must_save_reg = 0;
+ return 0;
+ }
+ }
+ if (z_dies_here)
+ {
+ info->need_save_z = 0;
+ info->z_died = 1;
+ if (info->last == 0)
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+ return info->last != NULL_RTX ? 0 : 1;
+ }
+ if (GET_CODE (body) == PARALLEL)
+ {
+ int i;
+ char ix_clobber = 0;
+ char iy_clobber = 0;
+ char z_clobber = 0;
+ this_insn_uses_iy = 0;
+ this_insn_uses_ix = 0;
+ this_insn_uses_z = 0;
+
+ for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+ {
+ rtx x;
+ int uses_ix, uses_iy, uses_z;
+
+ x = XVECEXP (body, 0, i);
+
+ if (info->can_use_d && reg_mentioned_p (d_reg, x))
+ info->can_use_d = 0;
+
+ uses_ix = reg_mentioned_p (ix_reg, x);
+ uses_iy = reg_mentioned_p (iy_reg, x);
+ uses_z = reg_mentioned_p (z_reg, x);
+ if (GET_CODE (x) == CLOBBER)
+ {
+ ix_clobber |= uses_ix;
+ iy_clobber |= uses_iy;
+ z_clobber |= uses_z;
+ }
+ else
+ {
+ this_insn_uses_ix |= uses_ix;
+ this_insn_uses_iy |= uses_iy;
+ this_insn_uses_z |= uses_z;
+ }
+ if (uses_z && GET_CODE (x) == SET)
+ {
+ rtx dst = XEXP (x, 0);
+
+ if (Z_REG_P (dst))
+ info->z_set_count++;
+ }
+ if (TARGET_M6812 && uses_z && side_effects_p (x))
+ info->need_save_z = 1;
+
+ if (z_clobber)
+ info->need_save_z = 0;
+ }
+ if (debug_m6811)
+ {
+ printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
+ this_insn_uses_ix, this_insn_uses_iy,
+ this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
+ debug_rtx (insn);
+ }
+ if (this_insn_uses_z)
+ info->can_use_d = 0;
+
+ if (z_clobber && info->first != insn)
+ {
+ info->need_save_z = 0;
+ info->last = insn;
+ return 0;
+ }
+ if (z_clobber && info->x_used == 0 && info->y_used == 0)
+ {
+ if (this_insn_uses_z == 0 && insn == info->first)
+ {
+ info->must_load_z = 0;
+ }
+ if (dead_register_here (insn, d_reg))
+ {
+ info->regno = HARD_D_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ else if (dead_register_here (insn, ix_reg))
+ {
+ info->regno = HARD_X_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ else if (dead_register_here (insn, iy_reg))
+ {
+ info->regno = HARD_Y_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ if (info->regno >= 0)
+ {
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+ if (this_insn_uses_ix == 0)
+ {
+ info->regno = HARD_X_REGNUM;
+ info->must_save_reg = 1;
+ info->must_restore_reg = 1;
+ }
+ else if (this_insn_uses_iy == 0)
+ {
+ info->regno = HARD_Y_REGNUM;
+ info->must_save_reg = 1;
+ info->must_restore_reg = 1;
+ }
+ else
+ {
+ info->regno = HARD_D_REGNUM;
+ info->must_save_reg = 1;
+ info->must_restore_reg = 1;
+ }
+ info->last = NEXT_INSN (insn);
+ return 0;
+ }
+
+ if (((info->x_used || this_insn_uses_ix) && iy_clobber)
+ || ((info->y_used || this_insn_uses_iy) && ix_clobber))
+ {
+ if (this_insn_uses_z)
+ {
+ if (info->y_used == 0 && iy_clobber)
+ {
+ info->regno = HARD_Y_REGNUM;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ if (info->first != insn
+ && ((info->y_used && ix_clobber)
+ || (info->x_used && iy_clobber)))
+ info->last = insn;
+ else
+ info->last = NEXT_INSN (insn);
+ info->save_before_last = 1;
+ }
+ return 0;
+ }
+ if (this_insn_uses_ix && this_insn_uses_iy)
+ {
+ if (this_insn_uses_z)
+ {
+ fatal_insn ("cannot do z-register replacement", insn);
+ }
+ return 0;
+ }
+ if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
+ {
+ if (info->y_used)
+ {
+ return 0;
+ }
+ info->x_used = 1;
+ if (iy_clobber || z_clobber)
+ {
+ info->last = NEXT_INSN (insn);
+ info->save_before_last = 1;
+ return 0;
+ }
+ }
+
+ if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
+ {
+ if (info->x_used)
+ {
+ return 0;
+ }
+ info->y_used = 1;
+ if (ix_clobber || z_clobber)
+ {
+ info->last = NEXT_INSN (insn);
+ info->save_before_last = 1;
+ return 0;
+ }
+ }
+ if (z_dies_here)
+ {
+ info->z_died = 1;
+ info->need_save_z = 0;
+ }
+ return 1;
+ }
+ if (GET_CODE (body) == CLOBBER)
+ {
+
+ /* IX and IY are used at the same time, we have to restore
+ the value of the scratch register before this insn. */
+ if (this_insn_uses_ix && this_insn_uses_iy)
+ {
+ return 0;
+ }
+ if (info->x_used == 0 && this_insn_uses_ix)
+ {
+ if (info->y_used)
+ {
+ return 0;
+ }
+ info->x_used = 1;
+ }
+ if (info->y_used == 0 && this_insn_uses_iy)
+ {
+ if (info->x_used)
+ {
+ return 0;
+ }
+ info->y_used = 1;
+ }
+ return 1;
+ }
+ return 1;
+}
+
+static void
+m68hc11_find_z_replacement (rtx insn, struct replace_info *info)
+{
+ int reg;
+
+ info->replace_reg = NULL_RTX;
+ info->must_load_z = 1;
+ info->need_save_z = 1;
+ info->must_save_reg = 1;
+ info->must_restore_reg = 1;
+ info->first = insn;
+ info->x_used = 0;
+ info->y_used = 0;
+ info->can_use_d = TARGET_M6811 ? 1 : 0;
+ info->found_call = 0;
+ info->z_died = 0;
+ info->last = 0;
+ info->regno = -1;
+ info->z_set_count = 0;
+ info->z_value = NULL_RTX;
+ info->must_push_reg = 0;
+ info->save_before_last = 0;
+ info->z_loaded_with_sp = 0;
+
+ /* Scan the insn forward to find an address register that is not used.
+ Stop when:
+ - the flow of the program changes,
+ - when we detect that both X and Y are necessary,
+ - when the Z register dies,
+ - when the condition codes are set. */
+
+ for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
+ {
+ if (m68hc11_check_z_replacement (insn, info) == 0)
+ break;
+ }
+
+ /* May be we can use Y or X if they contain the same value as Z.
+ This happens very often after the reload. */
+ if (info->z_set_count == 1)
+ {
+ rtx p = info->first;
+ rtx v = 0;
+
+ if (info->x_used)
+ {
+ v = find_last_value (iy_reg, &p, insn, 1);
+ }
+ else if (info->y_used)
+ {
+ v = find_last_value (ix_reg, &p, insn, 1);
+ }
+ if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
+ {
+ if (info->x_used)
+ info->regno = HARD_Y_REGNUM;
+ else
+ info->regno = HARD_X_REGNUM;
+ info->must_load_z = 0;
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ info->found_call = 1;
+ }
+ }
+ if (info->z_set_count == 0)
+ info->need_save_z = 0;
+
+ if (insn == 0)
+ info->need_save_z = 0;
+
+ if (info->last == 0)
+ info->last = insn;
+
+ if (info->regno >= 0)
+ {
+ reg = info->regno;
+ info->replace_reg = gen_rtx_REG (HImode, reg);
+ }
+ else if (info->can_use_d)
+ {
+ reg = HARD_D_REGNUM;
+ info->replace_reg = d_reg;
+ }
+ else if (info->x_used)
+ {
+ reg = HARD_Y_REGNUM;
+ info->replace_reg = iy_reg;
+ }
+ else
+ {
+ reg = HARD_X_REGNUM;
+ info->replace_reg = ix_reg;
+ }
+ info->regno = reg;
+
+ if (info->must_save_reg && info->must_restore_reg)
+ {
+ if (insn && dead_register_here (insn, info->replace_reg))
+ {
+ info->must_save_reg = 0;
+ info->must_restore_reg = 0;
+ }
+ }
+}
+
+/* The insn uses the Z register. Find a replacement register for it
+ (either X or Y) and replace it in the insn and the next ones until
+ the flow changes or the replacement register is used. Instructions
+ are emitted before and after the Z-block to preserve the value of
+ Z and of the replacement register. */
+
+static void
+m68hc11_z_replacement (rtx insn)
+{
+ rtx replace_reg_qi;
+ rtx replace_reg;
+ struct replace_info info;
+
+ /* Find trivial case where we only need to replace z with the
+ equivalent soft register. */
+ if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx body = PATTERN (insn);
+ rtx src = XEXP (body, 1);
+ rtx dst = XEXP (body, 0);
+
+ if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
+ {
+ XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
+ return;
+ }
+ else if (Z_REG_P (src)
+ && ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
+ {
+ XEXP (body, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
+ return;
+ }
+ else if (D_REG_P (dst)
+ && m68hc11_arith_operator (src, GET_MODE (src))
+ && D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
+ {
+ XEXP (src, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
+ return;
+ }
+ else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
+ && INTVAL (src) == 0)
+ {
+ XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
+ /* Force it to be re-recognized. */
+ INSN_CODE (insn) = -1;
+ return;
+ }
+ }
+
+ m68hc11_find_z_replacement (insn, &info);
+
+ replace_reg = info.replace_reg;
+ replace_reg_qi = NULL_RTX;
+
+ /* Save the X register in a .page0 location. */
+ if (info.must_save_reg && !info.must_push_reg)
+ {
+ rtx dst;
+
+ if (info.must_push_reg && 0)
+ dst = gen_rtx_MEM (HImode,
+ gen_rtx_PRE_DEC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ else
+ dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
+
+ emit_insn_before (gen_movhi (dst,
+ gen_rtx_REG (HImode, info.regno)), insn);
+ }
+ if (info.must_load_z && !info.must_push_reg)
+ {
+ emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
+ gen_rtx_REG (HImode, SOFT_Z_REGNUM)),
+ insn);
+ }
+
+
+ /* Replace all occurrence of Z by replace_reg.
+ Stop when the last instruction to replace is reached.
+ Also stop when we detect a change in the flow (but it's not
+ necessary; just safeguard). */
+
+ for (; insn && insn != info.last; insn = NEXT_INSN (insn))
+ {
+ rtx body;
+
+ if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
+ break;
+
+ if (GET_CODE (insn) != INSN
+ && GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
+ continue;
+
+ body = PATTERN (insn);
+ if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
+ || GET_CODE (body) == ASM_OPERANDS
+ || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx note;
+
+ if (debug_m6811 && reg_mentioned_p (replace_reg, body))
+ {
+ printf ("Reg mentioned here...:\n");
+ fflush (stdout);
+ debug_rtx (insn);
+ }
+
+ /* Stack pointer was decremented by 2 due to the push.
+ Correct that by adding 2 to the destination. */
+ if (info.must_push_reg
+ && info.z_loaded_with_sp && GET_CODE (body) == SET)
+ {
+ rtx src, dst;
+
+ src = SET_SRC (body);
+ dst = SET_DEST (body);
+ if (SP_REG_P (src) && Z_REG_P (dst))
+ emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
+ }
+
+ /* Replace any (REG:HI Z) occurrence by either X or Y. */
+ if (!validate_replace_rtx (z_reg, replace_reg, insn))
+ {
+ INSN_CODE (insn) = -1;
+ if (!validate_replace_rtx (z_reg, replace_reg, insn))
+ fatal_insn ("cannot do z-register replacement", insn);
+ }
+
+ /* Likewise for (REG:QI Z). */
+ if (reg_mentioned_p (z_reg, insn))
+ {
+ if (replace_reg_qi == NULL_RTX)
+ replace_reg_qi = gen_rtx_REG (QImode, REGNO (replace_reg));
+ validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
+ }
+
+ /* If there is a REG_INC note on Z, replace it with a
+ REG_INC note on the replacement register. This is necessary
+ to make sure that the flow pass will identify the change
+ and it will not remove a possible insn that saves Z. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ {
+ if (REG_NOTE_KIND (note) == REG_INC
+ && GET_CODE (XEXP (note, 0)) == REG
+ && REGNO (XEXP (note, 0)) == REGNO (z_reg))
+ {
+ XEXP (note, 0) = replace_reg;
+ }
+ }
+ }
+ if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+ break;
+ }
+
+ /* Save Z before restoring the old value. */
+ if (insn && info.need_save_z && !info.must_push_reg)
+ {
+ rtx save_pos_insn = insn;
+
+ /* If Z is clobber by the last insn, we have to save its value
+ before the last instruction. */
+ if (info.save_before_last)
+ save_pos_insn = PREV_INSN (save_pos_insn);
+
+ emit_insn_before (gen_movhi (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
+ gen_rtx_REG (HImode, info.regno)),
+ save_pos_insn);
+ }
+
+ if (info.must_push_reg && info.last)
+ {
+ rtx new_body, body;
+
+ body = PATTERN (info.last);
+ new_body = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (3, body,
+ gen_rtx_USE (VOIDmode,
+ replace_reg),
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_REG (HImode,
+ SOFT_Z_REGNUM))));
+ PATTERN (info.last) = new_body;
+
+ /* Force recognition on insn since we changed it. */
+ INSN_CODE (insn) = -1;
+
+ if (!validate_replace_rtx (z_reg, replace_reg, info.last))
+ {
+ fatal_insn ("invalid Z register replacement for insn", insn);
+ }
+ insn = NEXT_INSN (info.last);
+ }
+
+ /* Restore replacement register unless it was died. */
+ if (insn && info.must_restore_reg && !info.must_push_reg)
+ {
+ rtx dst;
+
+ if (info.must_push_reg && 0)
+ dst = gen_rtx_MEM (HImode,
+ gen_rtx_POST_INC (HImode,
+ gen_rtx_REG (HImode, HARD_SP_REGNUM)));
+ else
+ dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
+
+ emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
+ dst), insn);
+ }
+
+}
+
+
+/* Scan all the insn and re-affects some registers
+ - The Z register (if it was used), is affected to X or Y depending
+ on the instruction. */
+
+static void
+m68hc11_reassign_regs (rtx first)
+{
+ rtx insn;
+
+ ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
+ iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
+ z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
+ z_reg_qi = gen_rtx_REG (QImode, HARD_Z_REGNUM);
+
+ /* Scan all insns to replace Z by X or Y preserving the old value
+ of X/Y and restoring it afterward. */
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx body;
+
+ if (GET_CODE (insn) == CODE_LABEL
+ || GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
+ continue;
+
+ if (!INSN_P (insn))
+ continue;
+
+ body = PATTERN (insn);
+ if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
+ continue;
+
+ if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
+ || GET_CODE (body) == ASM_OPERANDS
+ || GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
+ continue;
+
+ if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
+ || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+ {
+
+ /* If Z appears in this insn, replace it in the current insn
+ and the next ones until the flow changes or we have to
+ restore back the replacement register. */
+
+ if (reg_mentioned_p (z_reg, body))
+ {
+ m68hc11_z_replacement (insn);
+ }
+ }
+ else
+ {
+ printf ("insn not handled by Z replacement:\n");
+ fflush (stdout);
+ debug_rtx (insn);
+ }
+ }
+}
+
+
+/* Machine-dependent reorg pass.
+ Specific optimizations are defined here:
+ - this pass changes the Z register into either X or Y
+ (it preserves X/Y previous values in a memory slot in page0).
+
+ When this pass is finished, the global variable
+ 'z_replacement_completed' is set to 2. */
+
+static void
+m68hc11_reorg (void)
+{
+ int split_done = 0;
+ rtx first;
+
+ z_replacement_completed = 0;
+ z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
+ first = get_insns ();
+
+ /* Some RTX are shared at this point. This breaks the Z register
+ replacement, unshare everything. */
+ unshare_all_rtl_again (first);
+
+ /* Force a split of all splittable insn. This is necessary for the
+ Z register replacement mechanism because we end up with basic insns. */
+ split_all_insns_noflow ();
+ split_done = 1;
+
+ z_replacement_completed = 1;
+ m68hc11_reassign_regs (first);
+
+ if (optimize)
+ compute_bb_for_insn ();
+
+ /* After some splitting, there are some opportunities for CSE pass.
+ This happens quite often when 32-bit or above patterns are split. */
+ if (optimize > 0 && split_done)
+ {
+ reload_cse_regs (first);
+ }
+
+ /* Re-create the REG_DEAD notes. These notes are used in the machine
+ description to use the best assembly directives. */
+ if (optimize)
+ {
+ df_note_add_problem ();
+ df_analyze ();
+ df_remove_problem (df_note);
+ }
+
+ z_replacement_completed = 2;
+
+ /* If optimizing, then go ahead and split insns that must be
+ split after Z register replacement. This gives more opportunities
+ for peephole (in particular for consecutives xgdx/xgdy). */
+ if (optimize > 0)
+ split_all_insns_noflow ();
+
+ /* Once insns are split after the z_replacement_completed == 2,
+ we must not re-run the life_analysis. The xgdx/xgdy patterns
+ are not recognized and the life_analysis pass removes some
+ insns because it thinks some (SETs) are noops or made to dead
+ stores (which is false due to the swap).
+
+ Do a simple pass to eliminate the noop set that the final
+ split could generate (because it was easier for split definition). */
+ {
+ rtx insn;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx body;
+
+ if (INSN_DELETED_P (insn))
+ continue;
+ if (!INSN_P (insn))
+ continue;
+
+ /* Remove the (set (R) (R)) insns generated by some splits. */
+ body = PATTERN (insn);
+ if (GET_CODE (body) == SET
+ && rtx_equal_p (SET_SRC (body), SET_DEST (body)))
+ {
+ SET_INSN_DELETED (insn);
+ continue;
+ }
+ }
+ }
+}
+
+/* Override memcpy */
+
+static void
+m68hc11_init_libfuncs (void)
+{
+ memcpy_libfunc = init_one_libfunc ("__memcpy");
+ memcmp_libfunc = init_one_libfunc ("__memcmp");
+ memset_libfunc = init_one_libfunc ("__memset");
+}
+
+
+
+/* Cost functions. */
+
+/* Cost of moving memory. */
+int
+m68hc11_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
+ int in ATTRIBUTE_UNUSED)
+{
+ if (rclass <= H_REGS && rclass > NO_REGS)
+ {
+ if (GET_MODE_SIZE (mode) <= 2)
+ return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
+ else
+ return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
+ }
+ else
+ {
+ if (GET_MODE_SIZE (mode) <= 2)
+ return COSTS_N_INSNS (3);
+ else
+ return COSTS_N_INSNS (4);
+ }
+}
+
+
+/* Cost of moving data from a register of class 'from' to on in class 'to'.
+ Reload does not check the constraint of set insns when the two registers
+ have a move cost of 2. Setting a higher cost will force reload to check
+ the constraints. */
+int
+m68hc11_register_move_cost (enum machine_mode mode, enum reg_class from,
+ enum reg_class to)
+{
+ /* All costs are symmetric, so reduce cases by putting the
+ lower number class as the destination. */
+ if (from < to)
+ {
+ enum reg_class tmp = to;
+ to = from, from = tmp;
+ }
+ if (to >= S_REGS)
+ return m68hc11_memory_move_cost (mode, S_REGS, 0);
+ else if (from <= S_REGS)
+ return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
+ else
+ return COSTS_N_INSNS (2);
+}
+
+
+/* Provide the costs of an addressing mode that contains ADDR.
+ If ADDR is not a valid address, its cost is irrelevant. */
+
+static int
+m68hc11_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+ int cost = 4;
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ /* Make the cost of hard registers and specially SP, FP small. */
+ if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
+ cost = 0;
+ else
+ cost = 1;
+ break;
+
+ case SYMBOL_REF:
+ cost = 8;
+ break;
+
+ case LABEL_REF:
+ case CONST:
+ cost = 0;
+ break;
+
+ case PLUS:
+ {
+ register rtx plus0 = XEXP (addr, 0);
+ register rtx plus1 = XEXP (addr, 1);
+
+ if (GET_CODE (plus0) != REG)
+ break;
+
+ switch (GET_CODE (plus1))
+ {
+ case CONST_INT:
+ if (INTVAL (plus1) >= 2 * m68hc11_max_offset
+ || INTVAL (plus1) < m68hc11_min_offset)
+ cost = 3;
+ else if (INTVAL (plus1) >= m68hc11_max_offset)
+ cost = 2;
+ else
+ cost = 1;
+ if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
+ cost += 0;
+ else
+ cost += 1;
+ break;
+
+ case SYMBOL_REF:
+ cost = 8;
+ break;
+
+ case CONST:
+ case LABEL_REF:
+ cost = 0;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ case PRE_DEC:
+ case PRE_INC:
+ if (SP_REG_P (XEXP (addr, 0)))
+ cost = 1;
+ break;
+
+ default:
+ break;
+ }
+ if (debug_m6811)
+ {
+ printf ("Address cost: %d for :", cost);
+ fflush (stdout);
+ debug_rtx (addr);
+ }
+
+ return cost;
+}
+
+static int
+m68hc11_shift_cost (enum machine_mode mode, rtx x, int shift)
+{
+ int total;
+
+ total = rtx_cost (x, SET, !optimize_size);
+ if (mode == QImode)
+ total += m68hc11_cost->shiftQI_const[shift % 8];
+ else if (mode == HImode)
+ total += m68hc11_cost->shiftHI_const[shift % 16];
+ else if (shift == 8 || shift == 16 || shift == 32)
+ total += m68hc11_cost->shiftHI_const[8];
+ else if (shift != 0 && shift != 16 && shift != 32)
+ {
+ total += m68hc11_cost->shiftHI_const[1] * shift;
+ }
+
+ /* For SI and others, the cost is higher. */
+ if (GET_MODE_SIZE (mode) > 2 && (shift % 16) != 0)
+ total *= GET_MODE_SIZE (mode) / 2;
+
+ /* When optimizing for size, make shift more costly so that
+ multiplications are preferred. */
+ if (optimize_size && (shift % 8) != 0)
+ total *= 2;
+
+ return total;
+}
+
+static int
+m68hc11_rtx_costs_1 (rtx x, enum rtx_code code,
+ enum rtx_code outer_code ATTRIBUTE_UNUSED)
+{
+ enum machine_mode mode = GET_MODE (x);
+ int extra_cost = 0;
+ int total;
+
+ switch (code)
+ {
+ case ROTATE:
+ case ROTATERT:
+ case ASHIFT:
+ case LSHIFTRT:
+ case ASHIFTRT:
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ return m68hc11_shift_cost (mode, XEXP (x, 0), INTVAL (XEXP (x, 1)));
+ }
+
+ total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
+ total += m68hc11_cost->shift_var;
+ return total;
+
+ case AND:
+ case XOR:
+ case IOR:
+ total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
+ total += m68hc11_cost->logical;
+
+ /* Logical instructions are byte instructions only. */
+ total *= GET_MODE_SIZE (mode);
+ return total;
+
+ case MINUS:
+ case PLUS:
+ total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
+ total += m68hc11_cost->add;
+ if (GET_MODE_SIZE (mode) > 2)
+ {
+ total *= GET_MODE_SIZE (mode) / 2;
+ }
+ return total;
+
+ case UDIV:
+ case DIV:
+ case MOD:
+ total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
+ switch (mode)
+ {
+ case QImode:
+ total += m68hc11_cost->divQI;
+ break;
+
+ case HImode:
+ total += m68hc11_cost->divHI;
+ break;
+
+ case SImode:
+ default:
+ total += m68hc11_cost->divSI;
+ break;
+ }
+ return total;
+
+ case MULT:
+ /* mul instruction produces 16-bit result. */
+ if (mode == HImode && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+ && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
+ return m68hc11_cost->multQI
+ + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
+ + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
+
+ /* emul instruction produces 32-bit result for 68HC12. */
+ if (TARGET_M6812 && mode == SImode
+ && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+ && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
+ return m68hc11_cost->multHI
+ + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
+ + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
+
+ total = rtx_cost (XEXP (x, 0), code, !optimize_size)
+ + rtx_cost (XEXP (x, 1), code, !optimize_size);
+ switch (mode)
+ {
+ case QImode:
+ total += m68hc11_cost->multQI;
+ break;
+
+ case HImode:
+ total += m68hc11_cost->multHI;
+ break;
+
+ case SImode:
+ default:
+ total += m68hc11_cost->multSI;
+ break;
+ }
+ return total;
+
+ case NEG:
+ case SIGN_EXTEND:
+ extra_cost = COSTS_N_INSNS (2);
+
+ /* Fall through */
+ case NOT:
+ case COMPARE:
+ case ABS:
+ case ZERO_EXTEND:
+ total = extra_cost + rtx_cost (XEXP (x, 0), code, !optimize_size);
+ if (mode == QImode)
+ {
+ return total + COSTS_N_INSNS (1);
+ }
+ if (mode == HImode)
+ {
+ return total + COSTS_N_INSNS (2);
+ }
+ if (mode == SImode)
+ {
+ return total + COSTS_N_INSNS (4);
+ }
+ return total + COSTS_N_INSNS (8);
+
+ case IF_THEN_ELSE:
+ if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
+ return COSTS_N_INSNS (1);
+
+ return COSTS_N_INSNS (1);
+
+ default:
+ return COSTS_N_INSNS (4);
+ }
+}
+
+static bool
+m68hc11_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ /* Constants are cheap. Moving them in registers must be avoided
+ because most instructions do not handle two register operands. */
+ case CONST_INT:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ /* Logical and arithmetic operations with a constant operand are
+ better because they are not supported with two registers. */
+ /* 'clr' is slow */
+ if (outer_code == SET && x == const0_rtx)
+ /* After reload, the reload_cse pass checks the cost to change
+ a SET into a PLUS. Make const0 cheap then. */
+ *total = 1 - reload_completed;
+ else
+ *total = 0;
+ return true;
+
+ case ROTATE:
+ case ROTATERT:
+ case ASHIFT:
+ case LSHIFTRT:
+ case ASHIFTRT:
+ case MINUS:
+ case PLUS:
+ case AND:
+ case XOR:
+ case IOR:
+ case UDIV:
+ case DIV:
+ case MOD:
+ case MULT:
+ case NEG:
+ case SIGN_EXTEND:
+ case NOT:
+ case COMPARE:
+ case ZERO_EXTEND:
+ case IF_THEN_ELSE:
+ *total = m68hc11_rtx_costs_1 (x, code, outer_code);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+/* Worker function for TARGET_ASM_FILE_START. */
+
+static void
+m68hc11_file_start (void)
+{
+ default_file_start ();
+
+ fprintf (asm_out_file, "\t.mode %s\n", TARGET_SHORT ? "mshort" : "mlong");
+}
+
+
+/* Worker function for TARGET_ASM_CONSTRUCTOR. */
+
+static void
+m68hc11_asm_out_constructor (rtx symbol, int priority)
+{
+ default_ctor_section_asm_out_constructor (symbol, priority);
+ fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
+}
+
+/* Worker function for TARGET_ASM_DESTRUCTOR. */
+
+static void
+m68hc11_asm_out_destructor (rtx symbol, int priority)
+{
+ default_dtor_section_asm_out_destructor (symbol, priority);
+ fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
+}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX. */
+
+static rtx
+m68hc11_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, HARD_D_REGNUM);
+}
+
+/* Return true if type TYPE should be returned in memory.
+ Blocks and data types largers than 4 bytes cannot be returned
+ in the register (D + X = 4). */
+
+static bool
+m68hc11_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+ if (TYPE_MODE (type) == BLKmode)
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (type);
+ return (size == -1 || size > 4);
+ }
+ else
+ return GET_MODE_SIZE (TYPE_MODE (type)) > 4;
+}
+
+#include "gt-m68hc11.h"
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11.h b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.h
new file mode 100644
index 000000000..d9935cc6e
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.h
@@ -0,0 +1,1538 @@
+/* Definitions of target machine for GNU compiler.
+ 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
+
+*/
+
+/*****************************************************************************
+**
+** Controlling the Compilation Driver, `gcc'
+**
+*****************************************************************************/
+
+#undef ENDFILE_SPEC
+
+/* Compile and assemble for a 68hc11 unless there is a -m68hc12 option. */
+#ifndef ASM_SPEC
+#define ASM_SPEC \
+"%{m68hc12:-m68hc12}" \
+"%{m68hcs12:-m68hcs12}" \
+"%{!m68hc12:%{!m68hcs12:-m68hc11}} " \
+"%{mshort:-mshort}%{!mshort:-mlong} " \
+"%{fshort-double:-mshort-double}%{!fshort-double:-mlong-double}"
+#endif
+
+/* We need to tell the linker the target elf format. Just pass an
+ emulation option. This can be overridden by -Wl option of gcc. */
+#ifndef LINK_SPEC
+#define LINK_SPEC \
+"%{m68hc12:-m m68hc12elf}" \
+"%{m68hcs12:-m m68hc12elf}" \
+"%{!m68hc12:%{!m68hcs12:-m m68hc11elf}} " \
+"%{!mnorelax:%{!m68hc12:%{!m68hcs12:-relax}}}"
+#endif
+
+#ifndef LIB_SPEC
+#define LIB_SPEC ""
+#endif
+
+#ifndef CC1_SPEC
+#define CC1_SPEC ""
+#endif
+
+#ifndef CPP_SPEC
+#define CPP_SPEC \
+"%{mshort:-D__HAVE_SHORT_INT__ -D__INT__=16}\
+ %{!mshort:-D__INT__=32}\
+ %{m68hc12:-Dmc6812 -DMC6812 -Dmc68hc12}\
+ %{m68hcs12:-Dmc6812 -DMC6812 -Dmc68hcs12}\
+ %{!m68hc12:%{!m68hcs12:-Dmc6811 -DMC6811 -Dmc68hc11}}\
+ %{fshort-double:-D__HAVE_SHORT_DOUBLE__}\
+ %{mlong-calls:-D__USE_RTC__}"
+#endif
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "crt1%O%s"
+
+/* Names to predefine in the preprocessor for this target machine. */
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_define_std ("mc68hc1x"); \
+ } \
+ while (0)
+
+/* As an embedded target, we have no libc. */
+#ifndef inhibit_libc
+# define inhibit_libc
+#endif
+
+/* Forward type declaration for prototypes definitions.
+ rtx_ptr is equivalent to rtx. Can't use the same name. */
+struct rtx_def;
+typedef struct rtx_def *rtx_ptr;
+
+union tree_node;
+typedef union tree_node *tree_ptr;
+
+/* We can't declare enum machine_mode forward nor include 'machmode.h' here.
+ Prototypes defined here will use an int instead. It's better than no
+ prototype at all. */
+typedef int enum_machine_mode;
+
+/*****************************************************************************
+**
+** Run-time Target Specification
+**
+*****************************************************************************/
+
+/* Run-time compilation parameters selecting different hardware subsets. */
+
+extern short *reg_renumber; /* def in local_alloc.c */
+
+#define TARGET_OP_TIME (optimize && optimize_size == 0)
+#define TARGET_RELAX (TARGET_NO_DIRECT_MODE)
+
+/* Default target_flags if no switches specified. */
+#ifndef TARGET_DEFAULT
+# define TARGET_DEFAULT 0
+#endif
+
+/* Define this macro as a C expression for the initializer of an
+ array of string to tell the driver program which options are
+ defaults for this target and thus do not need to be handled
+ specially when using `MULTILIB_OPTIONS'. */
+#ifndef MULTILIB_DEFAULTS
+# if TARGET_DEFAULT & MASK_M6811
+# define MULTILIB_DEFAULTS { "m68hc11" }
+# else
+# define MULTILIB_DEFAULTS { "m68hc12" }
+# endif
+#endif
+
+/* Print subsidiary information on the compiler version in use. */
+#define TARGET_VERSION fprintf (stderr, " (MC68HC11/MC68HC12/MC68HCS12)")
+
+/* Sometimes certain combinations of command options do not make
+ sense on a particular target machine. You can define a macro
+ `OVERRIDE_OPTIONS' to take account of this. This macro, if
+ defined, is executed once just after all the command options have
+ been parsed.
+
+ Don't use this macro to turn on various extra optimizations for
+ `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+
+#define OVERRIDE_OPTIONS m68hc11_override_options ()
+
+
+/* Define cost parameters for a given processor variant. */
+struct processor_costs {
+ const int add; /* cost of an add instruction */
+ const int logical; /* cost of a logical instruction */
+ const int shift_var;
+ const int shiftQI_const[8];
+ const int shiftHI_const[16];
+ const int multQI;
+ const int multHI;
+ const int multSI;
+ const int divQI;
+ const int divHI;
+ const int divSI;
+};
+
+/* Costs for the current processor. */
+extern const struct processor_costs *m68hc11_cost;
+
+
+/* target machine storage layout */
+
+/* Define this if most significant byte of a word is the lowest numbered. */
+#define BYTES_BIG_ENDIAN 1
+
+/* Define this if most significant bit is lowest numbered
+ in instructions that operate on numbered bit-fields. */
+#define BITS_BIG_ENDIAN 0
+
+/* Define this if most significant word of a multiword number is numbered. */
+#define WORDS_BIG_ENDIAN 1
+
+/* Width of a word, in units (bytes). */
+#define UNITS_PER_WORD 2
+
+/* Definition of size_t. This is really an unsigned short as the
+ 68hc11 only handles a 64K address space. */
+#define SIZE_TYPE "short unsigned int"
+
+/* A C expression for a string describing the name of the data type
+ to use for the result of subtracting two pointers. The typedef
+ name `ptrdiff_t' is defined using the contents of the string.
+ The 68hc11 only has a 64K address space. */
+#define PTRDIFF_TYPE "short int"
+
+/* Allocation boundary (bits) for storing pointers in memory. */
+#define POINTER_BOUNDARY 8
+
+/* Normal alignment required for function parameters on the stack, in bits.
+ This can't be less than BITS_PER_WORD */
+#define PARM_BOUNDARY (BITS_PER_WORD)
+
+/* Boundary (bits) on which stack pointer should be aligned. */
+#define STACK_BOUNDARY 8
+
+/* Allocation boundary (bits) for the code of a function. */
+#define FUNCTION_BOUNDARY 8
+
+#define BIGGEST_ALIGNMENT 8
+
+/* Alignment of field after `int : 0' in a structure. */
+#define EMPTY_FIELD_BOUNDARY 8
+
+/* Every structure's size must be a multiple of this. */
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+/* Define this if instructions will fail to work if given data not
+ on the nominal alignment. If instructions will merely go slower
+ in that case, do not define this macro. */
+#define STRICT_ALIGNMENT 0
+
+/* An integer expression for the size in bits of the largest integer
+ machine mode that should actually be used. All integer machine modes of
+ this size or smaller can be used for structures and unions with the
+ appropriate sizes. */
+#define MAX_FIXED_MODE_SIZE 64
+
+/* target machine storage layout */
+
+/* Size (bits) of the type "int" on target machine
+ (If undefined, default is BITS_PER_WORD). */
+#define INT_TYPE_SIZE (TARGET_SHORT ? 16 : 32)
+
+/* Size (bits) of the type "short" on target machine */
+#define SHORT_TYPE_SIZE 16
+
+/* Size (bits) of the type "long" on target machine */
+#define LONG_TYPE_SIZE 32
+
+/* Size (bits) of the type "long long" on target machine */
+#define LONG_LONG_TYPE_SIZE 64
+
+/* A C expression for the size in bits of the type `float' on the
+ target machine. If you don't define this, the default is one word.
+ Don't use default: a word is only 16. */
+#define FLOAT_TYPE_SIZE 32
+
+/* A C expression for the size in bits of the type double on the target
+ machine. If you don't define this, the default is two words.
+ Be IEEE compliant. */
+#define DOUBLE_TYPE_SIZE 64
+
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+/* Define this as 1 if `char' should by default be signed; else as 0. */
+#define DEFAULT_SIGNED_CHAR 0
+
+/* Define these to avoid dependence on meaning of `int'.
+ Note that WCHAR_TYPE_SIZE is used in cexp.y,
+ where TARGET_SHORT is not available. */
+#define WCHAR_TYPE "short int"
+#define WCHAR_TYPE_SIZE 16
+
+
+/* Standard register usage. */
+
+#define HARD_REG_SIZE (UNITS_PER_WORD)
+
+/* Assign names to real MC68HC11 registers.
+ A and B registers are not really used (A+B = D)
+ X register is first so that GCC allocates X+D for 32-bit integers and
+ the lowpart of that integer will be D. Having the lower part in D is
+ better for 32<->16bit conversions and for many arithmetic operations. */
+#define HARD_X_REGNUM 0
+#define HARD_D_REGNUM 1
+#define HARD_Y_REGNUM 2
+#define HARD_SP_REGNUM 3
+#define HARD_PC_REGNUM 4
+#define HARD_A_REGNUM 5
+#define HARD_B_REGNUM 6
+#define HARD_CCR_REGNUM 7
+
+/* The Z register does not really exist in the 68HC11. This a fake register
+ for GCC. It is treated exactly as an index register (X or Y). It is only
+ in the A_REGS class, which is the BASE_REG_CLASS for GCC. Defining this
+ register helps the reload pass of GCC. Otherwise, the reload often dies
+ with register spill failures.
+
+ The Z register is replaced by either X or Y during the machine specific
+ reorg (m68hc11_reorg). It is saved in the SOFT_Z_REGNUM soft-register
+ when this is necessary.
+
+ It's possible to tell GCC not to use this register with -ffixed-z. */
+#define HARD_Z_REGNUM 8
+
+/* The frame pointer is a soft-register. It's treated as such by GCC:
+ it is not and must not be part of the BASE_REG_CLASS. */
+#define DEFAULT_HARD_FP_REGNUM (9)
+#define HARD_FP_REGNUM (9)
+#define HARD_AP_REGNUM (HARD_FP_REGNUM)
+
+/* Temporary soft-register used in some cases when an operand came
+ up into a bad register class (D, X, Y, SP) and gcc failed to
+ recognize this. This register is never allocated by GCC. */
+#define SOFT_TMP_REGNUM 10
+
+/* The soft-register which is used to save the Z register
+ (see Z register replacement notes in m68hc11.c). */
+#define SOFT_Z_REGNUM 11
+
+/* The soft-register which is used to save either X or Y. */
+#define SOFT_SAVED_XY_REGNUM 12
+
+/* A fake clobber register for 68HC12 patterns. */
+#define FAKE_CLOBBER_REGNUM (13)
+
+/* Define 32 soft-registers of 16-bit each. By default,
+ only 12 of them are enabled and can be used by GCC. The
+ -msoft-reg-count=<n> option allows to control the number of valid
+ soft-registers. GCC can put 32-bit values in them
+ by allocating consecutive registers. The first 3 soft-registers
+ are never allocated by GCC. They are used in case the insn template needs
+ a temporary register, or for the Z register replacement. */
+
+#define MAX_SOFT_REG_COUNT (32)
+#define SOFT_REG_FIXED 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1
+#define SOFT_REG_USED 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1
+#define SOFT_REG_ORDER \
+SOFT_REG_FIRST, SOFT_REG_FIRST+1,SOFT_REG_FIRST+2,SOFT_REG_FIRST+3,\
+SOFT_REG_FIRST+4, SOFT_REG_FIRST+5,SOFT_REG_FIRST+6,SOFT_REG_FIRST+7,\
+SOFT_REG_FIRST+8, SOFT_REG_FIRST+9,SOFT_REG_FIRST+10,SOFT_REG_FIRST+11,\
+SOFT_REG_FIRST+12, SOFT_REG_FIRST+13,SOFT_REG_FIRST+14,SOFT_REG_FIRST+15,\
+SOFT_REG_FIRST+16, SOFT_REG_FIRST+17,SOFT_REG_FIRST+18,SOFT_REG_FIRST+19,\
+SOFT_REG_FIRST+20, SOFT_REG_FIRST+21,SOFT_REG_FIRST+22,SOFT_REG_FIRST+23,\
+SOFT_REG_FIRST+24, SOFT_REG_FIRST+25,SOFT_REG_FIRST+26,SOFT_REG_FIRST+27,\
+SOFT_REG_FIRST+28, SOFT_REG_FIRST+29,SOFT_REG_FIRST+30,SOFT_REG_FIRST+31
+
+#define SOFT_REG_NAMES \
+"*_.d1", "*_.d2", "*_.d3", "*_.d4", \
+"*_.d5", "*_.d6", "*_.d7", "*_.d8", \
+"*_.d9", "*_.d10", "*_.d11", "*_.d12", \
+"*_.d13", "*_.d14", "*_.d15", "*_.d16", \
+"*_.d17", "*_.d18", "*_.d19", "*_.d20", \
+"*_.d21", "*_.d22", "*_.d23", "*_.d24", \
+"*_.d25", "*_.d26", "*_.d27", "*_.d28", \
+"*_.d29", "*_.d30", "*_.d31", "*_.d32"
+
+/* First available soft-register for GCC. */
+#define SOFT_REG_FIRST (SOFT_SAVED_XY_REGNUM+2)
+
+/* Last available soft-register for GCC. */
+#define SOFT_REG_LAST (SOFT_REG_FIRST+MAX_SOFT_REG_COUNT)
+#define SOFT_FP_REGNUM (SOFT_REG_LAST)
+#define SOFT_AP_REGNUM (SOFT_FP_REGNUM+1)
+
+/* Number of actual hardware registers. The hardware registers are assigned
+ numbers for the compiler from 0 to just below FIRST_PSEUDO_REGISTER.
+ All registers that the compiler knows about must be given numbers, even
+ those that are not normally considered general registers. */
+#define FIRST_PSEUDO_REGISTER (SOFT_REG_LAST+2)
+
+/* 1 for registers that have pervasive standard uses and are not available
+ for the register allocator. */
+#define FIXED_REGISTERS \
+ {0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1,1, 1, SOFT_REG_FIXED, 1, 1}
+/* X, D, Y, SP,PC,A, B, CCR, Z, FP,ZTMP,ZR,XYR, FK, D1 - D32, SOFT-FP, AP */
+
+/* 1 for registers not available across function calls. For our pseudo
+ registers, all are available. */
+#define CALL_USED_REGISTERS \
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, SOFT_REG_USED, 1, 1}
+/* X, D, Y, SP,PC,A, B, CCR, Z, FP, ZTMP,ZR,XYR, D1 - 32, SOFT-FP, AP */
+
+
+/* Define this macro to change register usage conditional on target flags.
+
+ The soft-registers are disabled or enabled according to the
+ -msoft-reg-count=<n> option. */
+
+
+#define CONDITIONAL_REGISTER_USAGE (m68hc11_conditional_register_usage ())
+
+/* List the order in which to allocate registers. Each register must be
+ listed once, even those in FIXED_REGISTERS. */
+#define REG_ALLOC_ORDER \
+{ HARD_D_REGNUM, HARD_X_REGNUM, HARD_Y_REGNUM, \
+ SOFT_REG_ORDER, HARD_Z_REGNUM, HARD_PC_REGNUM, HARD_A_REGNUM, \
+ HARD_B_REGNUM, HARD_CCR_REGNUM, HARD_FP_REGNUM, SOFT_FP_REGNUM, \
+ HARD_SP_REGNUM, SOFT_TMP_REGNUM, SOFT_Z_REGNUM, SOFT_SAVED_XY_REGNUM, \
+ SOFT_AP_REGNUM, FAKE_CLOBBER_REGNUM }
+
+/* A C expression for the number of consecutive hard registers,
+ starting at register number REGNO, required to hold a value of
+ mode MODE. */
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+((Q_REGNO_P (REGNO)) ? (GET_MODE_SIZE (MODE)) : \
+ ((GET_MODE_SIZE (MODE) + HARD_REG_SIZE - 1) / HARD_REG_SIZE))
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+ - 8-bit values are stored anywhere (except the SP register).
+ - 16-bit values can be stored in any register whose mode is 16
+ - 32-bit values can be stored in D, X registers or in a soft register
+ (except the last one because we need 2 soft registers)
+ - Values whose size is > 32 bit are not stored in real hard
+ registers. They may be stored in soft registers if there are
+ enough of them. */
+#define HARD_REGNO_MODE_OK(REGNO, MODE) \
+ hard_regno_mode_ok (REGNO,MODE)
+
+/* Value is 1 if it is a good idea to tie two pseudo registers when one has
+ mode MODE1 and one has mode MODE2. If HARD_REGNO_MODE_OK could produce
+ different values for MODE1 and MODE2, for any hard reg, then this must be
+ 0 for correct output.
+
+ All modes are tieable except QImode. */
+#define MODES_TIEABLE_P(MODE1, MODE2) \
+ (((MODE1) == (MODE2)) \
+ || ((MODE1) != QImode && (MODE2) != QImode))
+
+
+/* Define the classes of registers for register constraints in the
+ machine description. Also define ranges of constants.
+
+ One of the classes must always be named ALL_REGS and include all hard regs.
+ If there is more than one class, another class must be named NO_REGS
+ and contain no registers.
+
+ The name GENERAL_REGS must be the name of a class (or an alias for
+ another name such as ALL_REGS). This is the class of registers
+ that is allowed by "g" or "r" in a register constraint.
+ Also, registers outside this class are allocated only when
+ instructions express preferences for them.
+
+ The classes must be numbered in nondecreasing order; that is,
+ a larger-numbered class must never be contained completely
+ in a smaller-numbered class.
+
+ For any two classes, it is very desirable that there be another
+ class that represents their union. */
+
+/* The M68hc11 has so few registers that it's not possible for GCC to
+ do any register allocation without breaking. We extend the processor
+ registers by having soft registers. These registers are treated as
+ hard registers by GCC but they are located in memory and accessed by page0
+ accesses (IND mode). */
+enum reg_class
+{
+ NO_REGS,
+ D_REGS, /* 16-bit data register */
+ X_REGS, /* 16-bit X register */
+ Y_REGS, /* 16-bit Y register */
+ SP_REGS, /* 16-bit stack pointer */
+ DA_REGS, /* 8-bit A reg. */
+ DB_REGS, /* 8-bit B reg. */
+ Z_REGS, /* 16-bit fake Z register */
+ D8_REGS, /* 8-bit A or B reg. */
+ Q_REGS, /* 8-bit (byte (QI)) data (A, B or D) */
+ D_OR_X_REGS, /* D or X register */
+ D_OR_Y_REGS, /* D or Y register */
+ D_OR_SP_REGS, /* D or SP register */
+ X_OR_Y_REGS, /* IX or Y register */
+ A_REGS, /* 16-bit address register (X, Y, Z) */
+ X_OR_SP_REGS, /* X or SP register */
+ Y_OR_SP_REGS, /* Y or SP register */
+ X_OR_Y_OR_D_REGS, /* X, Y or D */
+ A_OR_D_REGS, /* X, Y, Z or D */
+ A_OR_SP_REGS, /* X, Y, Z or SP */
+ H_REGS, /* 16-bit hard register (D, X, Y, Z, SP) */
+ S_REGS, /* 16-bit soft register */
+ D_OR_S_REGS, /* 16-bit soft register or D register */
+ X_OR_S_REGS, /* 16-bit soft register or X register */
+ Y_OR_S_REGS, /* 16-bit soft register or Y register */
+ Z_OR_S_REGS, /* 16-bit soft register or Z register */
+ SP_OR_S_REGS, /* 16-bit soft register or SP register */
+ D_OR_X_OR_S_REGS, /* 16-bit soft register or D or X register */
+ D_OR_Y_OR_S_REGS, /* 16-bit soft register or D or Y register */
+ D_OR_SP_OR_S_REGS, /* 16-bit soft register or D or SP register */
+ A_OR_S_REGS, /* 16-bit soft register or X, Y registers */
+ D_OR_A_OR_S_REGS, /* 16-bit soft register or D, X, Y registers */
+ TMP_REGS, /* 16-bit fake scratch register */
+ D_OR_A_OR_TMP_REGS, /* General scratch register */
+ G_REGS, /* 16-bit general register
+ (H_REGS + soft registers) */
+ ALL_REGS,
+ LIM_REG_CLASSES
+};
+
+/* alias GENERAL_REGS to G_REGS. */
+#define GENERAL_REGS G_REGS
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+/* Give names of register classes as strings for dump file. */
+#define REG_CLASS_NAMES \
+{ "NO_REGS", \
+ "D_REGS", \
+ "X_REGS", \
+ "Y_REGS", \
+ "SP_REGS", \
+ "DA_REGS", \
+ "DB_REGS", \
+ "D8_REGS", \
+ "Z_REGS", \
+ "Q_REGS", \
+ "D_OR_X_REGS", \
+ "D_OR_Y_REGS", \
+ "D_OR_SP_REGS", \
+ "X_OR_Y_REGS", \
+ "A_REGS", \
+ "X_OR_SP_REGS", \
+ "Y_OR_SP_REGS", \
+ "X_OR_Y_OR_D_REGS", \
+ "A_OR_D_REGS", \
+ "A_OR_SP_REGS", \
+ "H_REGS", \
+ "S_REGS", \
+ "D_OR_S_REGS", \
+ "X_OR_S_REGS", \
+ "Y_OR_S_REGS", \
+ "Z_OR_S_REGS", \
+ "SP_OR_S_REGS", \
+ "D_OR_X_OR_S_REGS", \
+ "D_OR_Y_OR_S_REGS", \
+ "D_OR_SP_OR_S_REGS", \
+ "A_OR_S_REGS", \
+ "D_OR_A_OR_S_REGS", \
+ "TMP_REGS", \
+ "D_OR_A_OR_TMP_REGS", \
+ "G_REGS", \
+ "ALL_REGS" }
+
+/* An initializer containing the contents of the register classes,
+ as integers which are bit masks. The Nth integer specifies the
+ contents of class N. The way the integer MASK is interpreted is
+ that register R is in the class if `MASK & (1 << R)' is 1. */
+
+/*--------------------------------------------------------------
+ X 0x00000001
+ D 0x00000002
+ Y 0x00000004
+ SP 0x00000008
+ PC 0x00000010
+ A 0x00000020
+ B 0x00000040
+ CCR 0x00000080
+ Z 0x00000100
+ FRAME 0x00000200
+ ZTMP 0x00000400
+ ZREG 0x00000800
+ XYREG 0x00001000
+ FAKE 0x00002000
+ Di 0xFFFFc000, 0x03FFF
+ SFRAME 0x00000000, 0x04000
+ AP 0x00000000, 0x08000
+
+ D_OR_X_REGS represents D+X. It is used for 32-bits numbers.
+ A_REGS represents a valid base register for indexing. It represents
+ X,Y and the Z register.
+ S_REGS represents the soft-registers. This includes the hard frame
+ and soft frame registers.
+--------------------------------------------------------------*/
+
+#define REG_CLASS_CONTENTS \
+/* NO_REGS */ {{ 0x00000000, 0x00000000 }, \
+/* D_REGS */ { 0x00000002, 0x00000000 }, /* D */ \
+/* X_REGS */ { 0x00000001, 0x00000000 }, /* X */ \
+/* Y_REGS */ { 0x00000004, 0x00000000 }, /* Y */ \
+/* SP_REGS */ { 0x00000008, 0x00000000 }, /* SP */ \
+/* DA_REGS */ { 0x00000020, 0x00000000 }, /* A */ \
+/* DB_REGS */ { 0x00000040, 0x00000000 }, /* B */ \
+/* Z_REGS */ { 0x00000100, 0x00000000 }, /* Z */ \
+/* D8_REGS */ { 0x00000060, 0x00000000 }, /* A B */ \
+/* Q_REGS */ { 0x00000062, 0x00000000 }, /* A B D */ \
+/* D_OR_X_REGS */ { 0x00000003, 0x00000000 }, /* D X */ \
+/* D_OR_Y_REGS */ { 0x00000006, 0x00000000 }, /* D Y */ \
+/* D_OR_SP_REGS */ { 0x0000000A, 0x00000000 }, /* D SP */ \
+/* X_OR_Y_REGS */ { 0x00000005, 0x00000000 }, /* X Y */ \
+/* A_REGS */ { 0x00000105, 0x00000000 }, /* X Y Z */ \
+/* X_OR_SP_REGS */ { 0x00000009, 0x00000000 }, /* X SP */ \
+/* Y_OR_SP_REGS */ { 0x0000000C, 0x00000000 }, /* Y SP */ \
+/* X_OR_Y_OR_D_REGS */ { 0x00000007, 0x00000000 }, /* D X Y */ \
+/* A_OR_D_REGS */ { 0x00000107, 0x00000000 }, /* D X Y Z */ \
+/* A_OR_SP_REGS */ { 0x0000010D, 0x00000000 }, /* X Y SP */ \
+/* H_REGS */ { 0x0000010F, 0x00000000 }, /* D X Y SP */ \
+/* S_REGS */ { 0xFFFFDE00, 0x00007FFF }, /* _.D,..,FP,Z* */ \
+/* D_OR_S_REGS */ { 0xFFFFDE02, 0x00007FFF }, /* D _.D */ \
+/* X_OR_S_REGS */ { 0xFFFFDE01, 0x00007FFF }, /* X _.D */ \
+/* Y_OR_S_REGS */ { 0xFFFFDE04, 0x00007FFF }, /* Y _.D */ \
+/* Z_OR_S_REGS */ { 0xFFFFDF00, 0x00007FFF }, /* Z _.D */ \
+/* SP_OR_S_REGS */ { 0xFFFFDE08, 0x00007FFF }, /* SP _.D */ \
+/* D_OR_X_OR_S_REGS */ { 0xFFFFDE03, 0x00007FFF }, /* D X _.D */ \
+/* D_OR_Y_OR_S_REGS */ { 0xFFFFDE06, 0x00007FFF }, /* D Y _.D */ \
+/* D_OR_SP_OR_S_REGS */ { 0xFFFFDE0A, 0x00007FFF }, /* D SP _.D */ \
+/* A_OR_S_REGS */ { 0xFFFFDF05, 0x00007FFF }, /* X Y _.D */ \
+/* D_OR_A_OR_S_REGS */ { 0xFFFFDF07, 0x00007FFF }, /* D X Y _.D */ \
+/* TMP_REGS */ { 0x00002000, 0x00000000 }, /* FAKE */ \
+/* D_OR_A_OR_TMP_REGS*/ { 0x00002107, 0x00000000 }, /* D X Y Z Fake */ \
+/* G_REGS */ { 0xFFFFFF1F, 0x00007FFF }, /* ? _.D D X Y */ \
+/* ALL_REGS*/ { 0xFFFFFFFF, 0x00007FFF }}
+
+
+/* set up a C expression whose value is a register class containing hard
+ register REGNO */
+#define Q_REGNO_P(REGNO) ((REGNO) == HARD_A_REGNUM \
+ || (REGNO) == HARD_B_REGNUM)
+#define Q_REG_P(X) (REG_P (X) && Q_REGNO_P (REGNO (X)))
+
+#define D_REGNO_P(REGNO) ((REGNO) == HARD_D_REGNUM)
+#define D_REG_P(X) (REG_P (X) && D_REGNO_P (REGNO (X)))
+
+#define DB_REGNO_P(REGNO) ((REGNO) == HARD_B_REGNUM)
+#define DB_REG_P(X) (REG_P (X) && DB_REGNO_P (REGNO (X)))
+#define DA_REGNO_P(REGNO) ((REGNO) == HARD_A_REGNUM)
+#define DA_REG_P(X) (REG_P (X) && DA_REGNO_P (REGNO (X)))
+
+#define X_REGNO_P(REGNO) ((REGNO) == HARD_X_REGNUM)
+#define X_REG_P(X) (REG_P (X) && X_REGNO_P (REGNO (X)))
+
+#define Y_REGNO_P(REGNO) ((REGNO) == HARD_Y_REGNUM)
+#define Y_REG_P(X) (REG_P (X) && Y_REGNO_P (REGNO (X)))
+
+#define Z_REGNO_P(REGNO) ((REGNO) == HARD_Z_REGNUM)
+#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X)))
+
+#define SP_REGNO_P(REGNO) ((REGNO) == HARD_SP_REGNUM)
+#define SP_REG_P(X) (REG_P (X) && SP_REGNO_P (REGNO (X)))
+
+/* Address register. */
+#define A_REGNO_P(REGNO) ((REGNO) == HARD_X_REGNUM \
+ || (REGNO) == HARD_Y_REGNUM \
+ || (REGNO) == HARD_Z_REGNUM)
+#define A_REG_P(X) (REG_P (X) && A_REGNO_P (REGNO (X)))
+
+/* M68hc11 hard registers. */
+#define H_REGNO_P(REGNO) (D_REGNO_P (REGNO) || A_REGNO_P (REGNO) \
+ || SP_REGNO_P (REGNO) || Q_REGNO_P (REGNO))
+#define H_REG_P(X) (REG_P (X) && H_REGNO_P (REGNO (X)))
+
+#define FAKE_REGNO_P(REGNO) ((REGNO) == FAKE_CLOBBER_REGNUM)
+#define FAKE_REG_P(X) (REG_P (X) && FAKE_REGNO_P (REGNO (X)))
+
+/* Soft registers (or register emulation for gcc). The temporary register
+ used by insn template must be part of the S_REGS class so that it
+ matches the 'u' constraint. */
+#define S_REGNO_P(REGNO) ((REGNO) >= SOFT_TMP_REGNUM \
+ && (REGNO) <= SOFT_REG_LAST \
+ && (REGNO) != FAKE_CLOBBER_REGNUM)
+#define S_REG_P(X) (REG_P (X) && S_REGNO_P (REGNO (X)))
+
+#define Z_REGNO_P(REGNO) ((REGNO) == HARD_Z_REGNUM)
+#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X)))
+
+/* General register. */
+#define G_REGNO_P(REGNO) (H_REGNO_P (REGNO) || S_REGNO_P (REGNO) \
+ || ((REGNO) == HARD_PC_REGNUM) \
+ || ((REGNO) == HARD_FP_REGNUM) \
+ || ((REGNO) == SOFT_FP_REGNUM) \
+ || ((REGNO) == FAKE_CLOBBER_REGNUM) \
+ || ((REGNO) == SOFT_AP_REGNUM))
+
+#define G_REG_P(X) (REG_P (X) && G_REGNO_P (REGNO (X)))
+
+#define REGNO_REG_CLASS(REGNO) \
+ (D_REGNO_P (REGNO) ? D_REGS : \
+ (X_REGNO_P (REGNO) ? X_REGS : \
+ (Y_REGNO_P (REGNO) ? Y_REGS : \
+ (SP_REGNO_P (REGNO) ? SP_REGS : \
+ (Z_REGNO_P (REGNO) ? Z_REGS : \
+ (H_REGNO_P (REGNO) ? H_REGS : \
+ (FAKE_REGNO_P (REGNO) ? TMP_REGS : \
+ (S_REGNO_P (REGNO) ? S_REGS : \
+ (DA_REGNO_P (REGNO) ? DA_REGS: \
+ (DB_REGNO_P (REGNO) ? DB_REGS: \
+ (G_REGNO_P (REGNO) ? G_REGS : ALL_REGS)))))))))))
+
+
+/* Get reg_class from a letter in the machine description. */
+
+extern enum reg_class m68hc11_tmp_regs_class;
+#define REG_CLASS_FROM_LETTER(C) \
+ ((C) == 'a' ? DA_REGS : \
+ (C) == 'A' ? A_REGS : \
+ (C) == 'b' ? DB_REGS : \
+ (C) == 'B' ? X_OR_Y_REGS : \
+ (C) == 'd' ? D_REGS : \
+ (C) == 'D' ? D_OR_X_REGS : \
+ (C) == 'q' ? Q_REGS : \
+ (C) == 'h' ? H_REGS : \
+ (C) == 't' ? TMP_REGS : \
+ (C) == 'u' ? S_REGS : \
+ (C) == 'v' ? m68hc11_tmp_regs_class : \
+ (C) == 'w' ? SP_REGS : \
+ (C) == 'x' ? X_REGS : \
+ (C) == 'y' ? Y_REGS : \
+ (C) == 'z' ? Z_REGS : NO_REGS)
+
+#define PREFERRED_RELOAD_CLASS(X,CLASS) preferred_reload_class(X,CLASS)
+
+#define SMALL_REGISTER_CLASSES 1
+
+/* A C expression that is nonzero if hard register number REGNO2 can be
+ considered for use as a rename register for REGNO1 */
+
+#define HARD_REGNO_RENAME_OK(REGNO1,REGNO2) \
+ m68hc11_hard_regno_rename_ok ((REGNO1), (REGNO2))
+
+/* A C expression whose value is nonzero if pseudos that have been
+ assigned to registers of class CLASS would likely be spilled
+ because registers of CLASS are needed for spill registers.
+
+ The default value of this macro returns 1 if CLASS has exactly one
+ register and zero otherwise. On most machines, this default
+ should be used. Only define this macro to some other expression
+ if pseudo allocated by `local-alloc.c' end up in memory because
+ their hard registers were needed for spill registers. If this
+ macro returns nonzero for those classes, those pseudos will only
+ be allocated by `global.c', which knows how to reallocate the
+ pseudo to another register. If there would not be another
+ register available for reallocation, you should not change the
+ definition of this macro since the only effect of such a
+ definition would be to slow down register allocation. */
+
+#define CLASS_LIKELY_SPILLED_P(CLASS) \
+ (((CLASS) == D_REGS) \
+ || ((CLASS) == X_REGS) \
+ || ((CLASS) == Y_REGS) \
+ || ((CLASS) == A_REGS) \
+ || ((CLASS) == SP_REGS) \
+ || ((CLASS) == D_OR_X_REGS) \
+ || ((CLASS) == D_OR_Y_REGS) \
+ || ((CLASS) == X_OR_SP_REGS) \
+ || ((CLASS) == Y_OR_SP_REGS) \
+ || ((CLASS) == D_OR_SP_REGS))
+
+/* Return the maximum number of consecutive registers needed to represent
+ mode MODE in a register of class CLASS. */
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+(((CLASS) == DA_REGS || (CLASS) == DB_REGS \
+ || (CLASS) == D8_REGS || (CLASS) == Q_REGS) ? GET_MODE_SIZE (MODE) \
+ : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+/* The letters I, J, K, L and M in a register constraint string
+ can be used to stand for particular ranges of immediate operands.
+ This macro defines what the ranges are.
+ C is the letter, and VALUE is a constant value.
+ Return 1 if VALUE is in the range specified by C.
+
+ `K' is for 0.
+ `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)
+
+ 'I', 'J' are not used. */
+
+#define CONST_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'K' ? (VALUE) == 0 : \
+ (C) == 'L' ? ((VALUE) >= -65536 && (VALUE) <= 65535) : \
+ (C) == 'M' ? ((VALUE) & 0x0ffffL) == 0 : \
+ (C) == 'N' ? ((VALUE) == 1 || (VALUE) == -1) : \
+ (C) == 'I' ? ((VALUE) >= -2 && (VALUE) <= 2) : \
+ (C) == 'O' ? (VALUE) == 16 : \
+ (C) == 'P' ? ((VALUE) <= 2 && (VALUE) >= -8) : 0)
+
+/* Similar, but for floating constants, and defining letters G and H.
+
+ `G' is for 0.0. */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'G' ? (GET_MODE_CLASS (GET_MODE (VALUE)) == MODE_FLOAT \
+ && VALUE == CONST0_RTX (GET_MODE (VALUE))) : 0)
+
+/* 'U' represents certain kind of memory indexed operand for 68HC12.
+ and any memory operand for 68HC11.
+ 'R' represents indexed addressing mode or access to page0 for 68HC11.
+ For 68HC12, it represents any memory operand. */
+#define EXTRA_CONSTRAINT(OP, C) \
+((C) == 'U' ? m68hc11_small_indexed_indirect_p (OP, GET_MODE (OP)) \
+ : (C) == 'Q' ? m68hc11_symbolic_p (OP, GET_MODE (OP)) \
+ : (C) == 'R' ? m68hc11_indirect_p (OP, GET_MODE (OP)) \
+ : (C) == 'S' ? (memory_operand (OP, GET_MODE (OP)) \
+ && non_push_operand (OP, GET_MODE (OP))) : 0)
+
+
+/* Stack layout; function entry, exit and calling. */
+
+/* Define this if pushing a word on the stack
+ makes the stack pointer a smaller address. */
+#define STACK_GROWS_DOWNWARD
+
+/* Define this to nonzero if the nominal address of the stack frame
+ is at the high-address end of the local variables;
+ that is, each additional local variable allocated
+ goes at a more negative offset in the frame.
+
+ Define to 0 for 68HC11, the frame pointer is the bottom
+ of local variables. */
+#define FRAME_GROWS_DOWNWARD 0
+
+/* Define this if successive arguments to a function occupy decreasing
+ addresses in the stack. */
+/* #define ARGS_GROW_DOWNWARD */
+
+/* Offset within stack frame to start allocating local variables at.
+ If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+ first local allocated. Otherwise, it is the offset to the BEGINNING
+ of the first local allocated. */
+#define STARTING_FRAME_OFFSET 0
+
+/* Offset of first parameter from the argument pointer register value. */
+
+#define FIRST_PARM_OFFSET(FNDECL) 2
+
+/* After the prologue, RA is at 0(AP) in the current frame. */
+#define RETURN_ADDR_RTX(COUNT, FRAME) \
+ ((COUNT) == 0 \
+ ? gen_rtx_MEM (Pmode, arg_pointer_rtx) \
+ : 0)
+
+/* Before the prologue, the top of the frame is at 2(sp). */
+#define INCOMING_FRAME_SP_OFFSET 2
+
+/* Define this if functions should assume that stack space has been
+ allocated for arguments even when their values are passed in
+ registers.
+
+ The value of this macro is the size, in bytes, of the area reserved for
+ arguments passed in registers.
+
+ This space can either be allocated by the caller or be a part of the
+ machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
+ says which. */
+/* #define REG_PARM_STACK_SPACE(FNDECL) 2 */
+
+/* Define this macro if REG_PARM_STACK_SPACE is defined but stack
+ parameters don't skip the area specified by REG_PARM_STACK_SPACE.
+ Normally, when a parameter is not passed in registers, it is placed on
+ the stack beyond the REG_PARM_STACK_SPACE area. Defining this macro
+ suppresses this behavior and causes the parameter to be passed on the
+ stack in its natural location. */
+/* #define STACK_PARMS_IN_REG_PARM_AREA */
+
+/* Register to use for pushing function arguments. */
+#define STACK_POINTER_REGNUM HARD_SP_REGNUM
+
+/* Base register for access to local variables of the function. */
+#define FRAME_POINTER_REGNUM SOFT_FP_REGNUM
+
+#define HARD_FRAME_POINTER_REGNUM HARD_FP_REGNUM
+
+/* Base register for access to arguments of the function. */
+#define ARG_POINTER_REGNUM SOFT_AP_REGNUM
+
+/* Register in which static-chain is passed to a function. */
+#define STATIC_CHAIN_REGNUM SOFT_Z_REGNUM
+
+
+/* Definitions for register eliminations.
+
+ This is an array of structures. Each structure initializes one pair
+ of eliminable registers. The "from" register number is given first,
+ followed by "to". Eliminations of the same "from" register are listed
+ in order of preference.
+
+ We have two registers that are eliminated on the 6811. The pseudo arg
+ pointer and pseudo frame pointer registers can always be eliminated;
+ they are replaced with either the stack or the real frame pointer. */
+
+#define ELIMINABLE_REGS \
+{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ {ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
+ {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ {FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
+
+/* Value should be nonzero if functions must have frame pointers.
+ Zero means the frame pointer need not be set up (and parms may be
+ accessed via the stack pointer) in functions that seem suitable.
+ This is computed in `reload', in reload1.c. */
+#define FRAME_POINTER_REQUIRED 0
+
+/* Given FROM and TO register numbers, say whether this elimination is allowed.
+ Frame pointer elimination is automatically handled.
+
+ All other eliminations are valid. */
+
+#define CAN_ELIMINATE(FROM, TO) \
+ ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
+ ? ! frame_pointer_needed \
+ : 1)
+
+
+/* Define the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+ { OFFSET = m68hc11_initial_elimination_offset (FROM, TO); }
+
+
+/* Passing Function Arguments on the Stack. */
+
+/* If we generate an insn to push BYTES bytes, this says how many the
+ stack pointer really advances by. No rounding or alignment needed
+ for MC6811. */
+#define PUSH_ROUNDING(BYTES) (BYTES)
+
+/* Value is 1 if returning from a function call automatically pops the
+ arguments described by the number-of-args field in the call. FUNTYPE is
+ the data type of the function (as a tree), or for a library call it is
+ an identifier node for the subroutine name.
+
+ The standard MC6811 call, with arg count word, includes popping the
+ args as part of the call template. */
+#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
+
+/* Passing Arguments in Registers. */
+
+/* Define a data type for recording info about an argument list
+ during the scan of that argument list. This data type should
+ hold all necessary information about the function itself
+ and about the args processed so far, enough to enable macros
+ such as FUNCTION_ARG to determine where the next arg should go. */
+
+typedef struct m68hc11_args
+{
+ int words;
+ int nregs;
+} CUMULATIVE_ARGS;
+
+/* If defined, a C expression which determines whether, and in which direction,
+ to pad out an argument with extra space. The value should be of type
+ `enum direction': either `upward' to pad above the argument,
+ `downward' to pad below, or `none' to inhibit padding.
+
+ Structures are stored left shifted in their argument slot. */
+#define FUNCTION_ARG_PADDING(MODE, TYPE) \
+ m68hc11_function_arg_padding ((MODE), (TYPE))
+
+#undef PAD_VARARGS_DOWN
+#define PAD_VARARGS_DOWN \
+ (m68hc11_function_arg_padding (TYPE_MODE (type), type) == downward)
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a
+ function whose data type is FNTYPE. For a library call, FNTYPE is 0. */
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
+ (m68hc11_init_cumulative_args (&CUM, FNTYPE, LIBNAME))
+
+/* Update the data in CUM to advance over an argument of mode MODE and data
+ type TYPE. (TYPE is null for libcalls where that information may not be
+ available.) */
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+ (m68hc11_function_arg_advance (&CUM, MODE, TYPE, NAMED))
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+ (m68hc11_function_arg (&CUM, MODE, TYPE, NAMED))
+
+/* Define the profitability of saving registers around calls.
+
+ Disable this because the saving instructions generated by
+ caller-save need a reload and the way it is implemented,
+ it forbids all spill registers at that point. Enabling
+ caller saving results in spill failure. */
+#define CALLER_SAVE_PROFITABLE(REFS,CALLS) 0
+
+/* 1 if N is a possible register number for function argument passing.
+ D is for 16-bit values, X is for 32-bit (X+D). */
+#define FUNCTION_ARG_REGNO_P(N) \
+ (((N) == HARD_D_REGNUM) || ((N) == HARD_X_REGNUM))
+
+/* All return values are in the D or X+D registers:
+ - 8 and 16-bit values are returned in D.
+ BLKmode are passed in D as pointer.
+ - 32-bit values are returned in X + D.
+ The high part is passed in X and the low part in D.
+ For GCC, the register number must be HARD_X_REGNUM. */
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ gen_rtx_REG (TYPE_MODE (VALTYPE), \
+ ((TYPE_MODE (VALTYPE) == BLKmode \
+ || GET_MODE_SIZE (TYPE_MODE (VALTYPE)) <= 2) \
+ ? HARD_D_REGNUM : HARD_X_REGNUM))
+
+#define LIBCALL_VALUE(MODE) \
+ gen_rtx_REG (MODE, \
+ (((MODE) == BLKmode || GET_MODE_SIZE (MODE) <= 2) \
+ ? HARD_D_REGNUM : HARD_X_REGNUM))
+
+/* 1 if N is a possible register number for a function value. */
+#define FUNCTION_VALUE_REGNO_P(N) \
+ ((N) == HARD_D_REGNUM || (N) == HARD_X_REGNUM)
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+ the stack pointer does not matter. The value is tested only in functions
+ that have frame pointers. No definition is equivalent to always zero. */
+#define EXIT_IGNORE_STACK 0
+
+
+/* Generating Code for Profiling. */
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+ for profiling a function entry. */
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+ fprintf (FILE, "\tldy\t.LP%d\n\tjsr mcount\n", (LABELNO))
+/* Length in units of the trampoline for entering a nested function. */
+#define TRAMPOLINE_SIZE (TARGET_M6811 ? 11 : 9)
+
+/* A C statement to initialize the variable parts of a trampoline.
+ ADDR is an RTX for the address of the trampoline; FNADDR is an
+ RTX for the address of the nested function; STATIC_CHAIN is an
+ RTX for the static chain value that should be passed to the
+ function when it is called. */
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
+ m68hc11_initialize_trampoline ((TRAMP), (FNADDR), (CXT))
+
+
+/* Addressing modes, and classification of registers for them. */
+
+#define ADDR_STRICT 0x01 /* Accept only registers in class A_REGS */
+#define ADDR_INCDEC 0x02 /* Post/Pre inc/dec */
+#define ADDR_INDEXED 0x04 /* D-reg index */
+#define ADDR_OFFSET 0x08
+#define ADDR_INDIRECT 0x10 /* Accept (mem (mem ...)) for [n,X] */
+#define ADDR_CONST 0x20 /* Accept const and symbol_ref */
+
+/* The 68HC12 has all the post/pre increment/decrement modes. */
+#define HAVE_POST_INCREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_PRE_INCREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_POST_DECREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_PRE_DECREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+
+/* The class value for base registers. This depends on the target:
+ A_REGS for 68HC11 and A_OR_SP_REGS for 68HC12. The class value
+ is stored at init time. */
+extern enum reg_class m68hc11_base_reg_class;
+#define BASE_REG_CLASS m68hc11_base_reg_class
+
+/* The class value for index registers. This is NO_REGS for 68HC11. */
+
+extern enum reg_class m68hc11_index_reg_class;
+#define INDEX_REG_CLASS m68hc11_index_reg_class
+
+/* These assume that REGNO is a hard or pseudo reg number. They give nonzero
+ only if REGNO is a hard reg of the suitable class or a pseudo reg currently
+ allocated to a suitable hard reg. Since they use reg_renumber, they are
+ safe only once reg_renumber has been allocated, which happens in
+ local-alloc.c. */
+
+
+extern unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
+#define REG_VALID_FOR_BASE_P(REGNO) \
+ ((REGNO) < FIRST_PSEUDO_REGISTER \
+ && m68hc11_reg_valid_for_base[REGNO])
+
+/* Internal macro, return 1 if REGNO is a valid index register. */
+extern unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
+#define REG_VALID_FOR_INDEX_P(REGNO) \
+ ((REGNO) < FIRST_PSEUDO_REGISTER \
+ && m68hc11_reg_valid_for_index[REGNO])
+
+/* Internal macro, the nonstrict definition for REGNO_OK_FOR_BASE_P. */
+#define REGNO_OK_FOR_BASE_NONSTRICT_P(REGNO) \
+ ((REGNO) >= FIRST_PSEUDO_REGISTER \
+ || REG_VALID_FOR_BASE_P (REGNO) \
+ || (REGNO) == FRAME_POINTER_REGNUM \
+ || (REGNO) == HARD_FRAME_POINTER_REGNUM \
+ || (REGNO) == ARG_POINTER_REGNUM \
+ || (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
+
+/* Internal macro, the nonstrict definition for REGNO_OK_FOR_INDEX_P. */
+#define REGNO_OK_FOR_INDEX_NONSTRICT_P(REGNO) \
+ (TARGET_M6812 \
+ && ((REGNO) >= FIRST_PSEUDO_REGISTER \
+ || REG_VALID_FOR_INDEX_P (REGNO) \
+ || (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
+
+/* Internal macro, the strict definition for REGNO_OK_FOR_BASE_P. */
+#define REGNO_OK_FOR_BASE_STRICT_P(REGNO) \
+ ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_BASE_P (REGNO) \
+ : (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
+
+/* Internal macro, the strict definition for REGNO_OK_FOR_INDEX_P. */
+#define REGNO_OK_FOR_INDEX_STRICT_P(REGNO) \
+ (TARGET_M6812 \
+ && ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_INDEX_P (REGNO) \
+ : (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
+
+#define REGNO_OK_FOR_BASE_P2(REGNO,STRICT) \
+ ((STRICT) ? (REGNO_OK_FOR_BASE_STRICT_P (REGNO)) \
+ : (REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO)))
+
+#define REGNO_OK_FOR_INDEX_P2(REGNO,STRICT) \
+ ((STRICT) ? (REGNO_OK_FOR_INDEX_STRICT_P (REGNO)) \
+ : (REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO)))
+
+#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_BASE_STRICT_P (REGNO)
+#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_INDEX_STRICT_P (REGNO)
+
+#define REG_OK_FOR_BASE_STRICT_P(X) REGNO_OK_FOR_BASE_STRICT_P (REGNO (X))
+#define REG_OK_FOR_BASE_NONSTRICT_P(X) REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO (X))
+#define REG_OK_FOR_INDEX_STRICT_P(X) REGNO_OK_FOR_INDEX_STRICT_P (REGNO (X))
+#define REG_OK_FOR_INDEX_NONSTRICT_P(X) REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO (X))
+
+/* see PUSH_POP_ADDRESS_P() below for an explanation of this. */
+#define IS_STACK_PUSH(operand) \
+ ((GET_CODE (operand) == MEM) \
+ && (GET_CODE (XEXP (operand, 0)) == PRE_DEC) \
+ && (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
+
+#define IS_STACK_POP(operand) \
+ ((GET_CODE (operand) == MEM) \
+ && (GET_CODE (XEXP (operand, 0)) == POST_INC) \
+ && (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
+
+/* 1 if X is an rtx for a constant that is a valid address. */
+#define CONSTANT_ADDRESS_P(X) (CONSTANT_P (X))
+
+/* Maximum number of registers that can appear in a valid memory address */
+#define MAX_REGS_PER_ADDRESS 2
+
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a
+ valid memory address for an instruction. The MODE argument is the
+ machine mode for the MEM expression that wants to use this address. */
+
+/*--------------------------------------------------------------
+ Valid addresses are either direct or indirect (MEM) versions
+ of the following forms:
+ constant N
+ register ,X
+ indexed N,X
+--------------------------------------------------------------*/
+
+/* The range of index that is allowed by indirect addressing. */
+
+#define VALID_MIN_OFFSET m68hc11_min_offset
+#define VALID_MAX_OFFSET m68hc11_max_offset
+
+/* The offset values which are allowed by the n,x and n,y addressing modes.
+ Take into account the size of the mode because we may have to add
+ a mode offset to access the lowest part of the data.
+ (For example, for an SImode, the last valid offset is 252.) */
+#define VALID_CONSTANT_OFFSET_P(X,MODE) \
+(((GET_CODE (X) == CONST_INT) && \
+ ((INTVAL (X) >= VALID_MIN_OFFSET) \
+ && ((INTVAL (X) <= VALID_MAX_OFFSET \
+ - (HOST_WIDE_INT) (GET_MODE_SIZE (MODE) + 1))))) \
+|| (TARGET_M6812 \
+ && ((GET_CODE (X) == SYMBOL_REF) \
+ || GET_CODE (X) == LABEL_REF \
+ || GET_CODE (X) == CONST)))
+
+/* This is included to allow stack push/pop operations. Special hacks in the
+ md and m6811.c files exist to support this. */
+#define PUSH_POP_ADDRESS_P(X) \
+ (((GET_CODE (X) == PRE_DEC) || (GET_CODE (X) == POST_INC)) \
+ && SP_REG_P (XEXP (X, 0)))
+
+/* Go to ADDR if X is a valid address. */
+#ifndef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (m68hc11_go_if_legitimate_address ((X), (MODE), 0)) goto ADDR; \
+}
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (m68hc11_go_if_legitimate_address ((X), (MODE), 1)) goto ADDR; \
+}
+#endif
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its
+ validity for a certain class. We have two alternate definitions for each
+ of them. The usual definition accepts all pseudo regs; the other rejects
+ them unless they have been allocated suitable hard regs. The symbol
+ REG_OK_STRICT causes the latter definition to be used.
+
+ Most source files want to accept pseudo regs in the hope that they will
+ get allocated to the class that the insn wants them to be in. Source files
+ for reload pass need to be strict. After reload, it makes no difference,
+ since pseudo regs have been eliminated by then. */
+
+#ifndef REG_OK_STRICT
+/* Nonzero if X is a hard reg that can be used as a base reg. */
+#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P(X)
+
+/* Nonzero if X is a hard reg that can be used as an index. */
+#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P(X)
+#else
+#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P(X)
+#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_STRICT_P(X)
+#endif
+
+
+/* Try machine-dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new, valid address.
+ This macro is used in only one place: `memory_address' in explow.c.
+
+ OLDX is the address as it was before break_out_memory_refs was called.
+ In some cases it is useful to look at this to decide what needs to be done.
+
+ MODE and WIN are passed so that this macro can use
+ GO_IF_LEGITIMATE_ADDRESS.
+
+ It is always safe for this macro to do nothing.
+ It exists to recognize opportunities to optimize the output. */
+
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
+{ rtx operand = (X); \
+ if (m68hc11_legitimize_address (&operand, (OLDX), (MODE))) \
+ { \
+ (X) = operand; \
+ GO_IF_LEGITIMATE_ADDRESS (MODE,X,WIN); \
+ } \
+}
+
+/* Go to LABEL if ADDR (a legitimate address expression)
+ has an effect that depends on the machine mode it is used for. */
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)
+
+/* Nonzero if the constant value X is a legitimate general operand.
+ It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
+
+#define LEGITIMATE_CONSTANT_P(X) 1
+
+
+/* Tell final.c how to eliminate redundant test instructions. */
+
+#define NOTICE_UPDATE_CC(EXP, INSN) \
+ m68hc11_notice_update_cc ((EXP), (INSN))
+
+/* Move costs between classes of registers */
+#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
+ (m68hc11_register_move_cost (MODE, CLASS1, CLASS2))
+
+/* Move cost between register and memory.
+ - Move to a 16-bit register is reasonable,
+ - Move to a soft register can be expensive. */
+#define MEMORY_MOVE_COST(MODE,CLASS,IN) \
+ m68hc11_memory_move_cost ((MODE),(CLASS),(IN))
+
+/* A C expression for the cost of a branch instruction. A value of 1
+ is the default; other values are interpreted relative to that.
+
+ Pretend branches are cheap because GCC generates sub-optimal code
+ for the default value. */
+#define BRANCH_COST(speed_p, predictable_p) 0
+
+/* Nonzero if access to memory by bytes is slow and undesirable. */
+#define SLOW_BYTE_ACCESS 0
+
+/* It is as good to call a constant function address as to call an address
+ kept in a register. */
+#define NO_FUNCTION_CSE
+
+/* Try a machine-dependent way of reloading an illegitimate address
+ operand. If we find one, push the reload and jump to WIN. This
+ macro is used in only one place: `find_reloads_address' in reload.c.
+
+ For M68HC11, we handle large displacements of a base register
+ by splitting the addend across an addhi3 insn.
+
+ For M68HC12, the 64K offset range is available.
+ */
+
+#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN) \
+do { \
+ /* We must recognize output that we have already generated ourselves. */ \
+ if (GET_CODE (X) == PLUS \
+ && GET_CODE (XEXP (X, 0)) == PLUS \
+ && GET_CODE (XEXP (XEXP (X, 0), 0)) == REG \
+ && GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT \
+ && GET_CODE (XEXP (X, 1)) == CONST_INT) \
+ { \
+ push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL, \
+ BASE_REG_CLASS, GET_MODE (X), VOIDmode, 0, 0, \
+ OPNUM, TYPE); \
+ goto WIN; \
+ } \
+ if (GET_CODE (X) == PLUS \
+ && GET_CODE (XEXP (X, 0)) == REG \
+ && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && !VALID_CONSTANT_OFFSET_P (XEXP (X, 1), MODE)) \
+ { \
+ HOST_WIDE_INT val = INTVAL (XEXP (X, 1)); \
+ HOST_WIDE_INT low, high; \
+ high = val & (~0x0FF); \
+ low = val & 0x00FF; \
+ if (low >= 256-15) { high += 16; low -= 16; } \
+ /* Reload the high part into a base reg; leave the low part \
+ in the mem directly. */ \
+ \
+ X = gen_rtx_PLUS (Pmode, \
+ gen_rtx_PLUS (Pmode, XEXP (X, 0), \
+ GEN_INT (high)), \
+ GEN_INT (low)); \
+ \
+ push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL, \
+ BASE_REG_CLASS, GET_MODE (X), VOIDmode, 0, 0, \
+ OPNUM, TYPE); \
+ goto WIN; \
+ } \
+} while (0)
+
+
+/* Defining the Output Assembler Language. */
+
+/* A default list of other sections which we might be "in" at any given
+ time. For targets that use additional sections (e.g. .tdesc) you
+ should override this definition in the target-specific file which
+ includes this file. */
+
+/* Output before read-only data. */
+#define TEXT_SECTION_ASM_OP ("\t.sect\t.text")
+
+/* Output before writable data. */
+#define DATA_SECTION_ASM_OP ("\t.sect\t.data")
+
+/* Output before uninitialized data. */
+#define BSS_SECTION_ASM_OP ("\t.sect\t.bss")
+
+/* Define the pseudo-ops used to switch to the .ctors and .dtors sections.
+
+ Same as config/elfos.h but don't mark these section SHF_WRITE since
+ there is no shared library problem. */
+#undef CTORS_SECTION_ASM_OP
+#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"a\""
+
+#undef DTORS_SECTION_ASM_OP
+#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"a\""
+
+#define TARGET_ASM_CONSTRUCTOR m68hc11_asm_out_constructor
+#define TARGET_ASM_DESTRUCTOR m68hc11_asm_out_destructor
+
+/* Comment character */
+#define ASM_COMMENT_START ";"
+
+/* Output to assembler file text saying following lines
+ may contain character constants, extra white space, comments, etc. */
+#define ASM_APP_ON "; Begin inline assembler code\n#APP\n"
+
+/* Output to assembler file text saying following lines
+ no longer contain unusual constructs. */
+#define ASM_APP_OFF "; End of inline assembler code\n#NO_APP\n"
+
+/* Write the extra assembler code needed to declare a function properly.
+ Some svr4 assemblers need to also have something extra said about the
+ function's return value. We allow for that here.
+
+ For 68HC12 we mark functions that return with 'rtc'. The linker
+ will ensure that a 'call' is really made (instead of 'jsr').
+ The debugger needs this information to correctly compute the stack frame.
+
+ For 68HC11/68HC12 we also mark interrupt handlers for gdb to
+ compute the correct stack frame. */
+
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
+ do \
+ { \
+ fprintf (FILE, "%s", TYPE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ putc (',', FILE); \
+ fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
+ putc ('\n', FILE); \
+ \
+ if (current_function_far) \
+ { \
+ fprintf (FILE, "\t.far\t"); \
+ assemble_name (FILE, NAME); \
+ putc ('\n', FILE); \
+ } \
+ else if (current_function_interrupt \
+ || current_function_trap) \
+ { \
+ fprintf (FILE, "\t.interrupt\t"); \
+ assemble_name (FILE, NAME); \
+ putc ('\n', FILE); \
+ } \
+ ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } \
+ while (0)
+
+/* Output #ident as a .ident. */
+
+/* output external reference */
+#undef ASM_OUTPUT_EXTERNAL
+#define ASM_OUTPUT_EXTERNAL(FILE,DECL,NAME) \
+ {fputs ("\t; extern\t", FILE); \
+ assemble_name (FILE, NAME); \
+ fputs ("\n", FILE);}
+
+/* How to refer to registers in assembler output. This sequence is indexed
+ by compiler's hard-register-number (see above). */
+#define REGISTER_NAMES \
+{ "x", "d", "y", "sp", "pc", "a", "b", "ccr", "z", \
+ "*_.frame", "*_.tmp", "*_.z", "*_.xy", "*fake clobber", \
+ SOFT_REG_NAMES, "*sframe", "*ap"}
+
+/* Print an instruction operand X on file FILE. CODE is the code from the
+ %-spec for printing this operand. If `%z3' was used to print operand
+ 3, then CODE is 'z'. */
+
+#define PRINT_OPERAND(FILE, X, CODE) \
+ print_operand (FILE, X, CODE)
+
+/* Print a memory operand whose address is X, on file FILE. */
+#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \
+ print_operand_address (FILE, ADDR)
+
+/* This is how to output an insn to push/pop a register on the stack.
+ It need not be very fast code.
+
+ Don't define because we don't know how to handle that with
+ the STATIC_CHAIN_REGNUM (soft register). Saving the static
+ chain must be made inside FUNCTION_PROFILER. */
+
+#undef ASM_OUTPUT_REG_PUSH
+#undef ASM_OUTPUT_REG_POP
+
+/* This is how to output an element of a case-vector that is relative. */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+ fprintf (FILE, "\t%s\tL%d-L%d\n", integer_asm_op (2, TRUE), VALUE, REL)
+
+/* This is how to output an element of a case-vector that is absolute. */
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+ fprintf (FILE, "\t%s\t.L%d\n", integer_asm_op (2, TRUE), VALUE)
+
+/* This is how to output an assembler line that says to advance the
+ location counter to a multiple of 2**LOG bytes. */
+#define ASM_OUTPUT_ALIGN(FILE,LOG) \
+ do { \
+ if ((LOG) > 1) \
+ fprintf ((FILE), "%s\n", ALIGN_ASM_OP); \
+ } while (0)
+
+
+/* Assembler Commands for Exception Regions. */
+
+/* Default values provided by GCC should be ok. Assuming that DWARF-2
+ frame unwind info is ok for this platform. */
+
+#undef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+/* For the support of memory banks we need addresses that indicate
+ the page number. */
+#define DWARF2_ADDR_SIZE 4
+
+/* SCz 2003-07-08: Don't use as dwarf2 .file/.loc directives because
+ the linker is doing relaxation and it does not adjust the debug_line
+ sections when it shrinks the code. This results in invalid addresses
+ when debugging. This does not bless too much the HC11/HC12 as most
+ applications are embedded and small, hence a reasonable debug info.
+ This problem is known for binutils 2.13, 2.14 and mainline. */
+#undef HAVE_AS_DWARF2_DEBUG_LINE
+
+/* The prefix for local labels. You should be able to define this as
+ an empty string, or any arbitrary string (such as ".", ".L%", etc)
+ without having to make any other changes to account for the specific
+ definition. Note it is a string literal, not interpreted by printf
+ and friends. */
+#define LOCAL_LABEL_PREFIX "."
+
+/* The prefix for immediate operands. */
+#define IMMEDIATE_PREFIX "#"
+#define GLOBAL_ASM_OP "\t.globl\t"
+
+
+/* Miscellaneous Parameters. */
+
+/* Specify the machine mode that this machine uses
+ for the index in the tablejump instruction. */
+#define CASE_VECTOR_MODE Pmode
+
+/* This flag, if defined, says the same insns that convert to a signed fixnum
+ also convert validly to an unsigned one. */
+#define FIXUNS_TRUNC_LIKE_FIX_TRUNC
+
+/* Max number of bytes we can move from memory to memory in one
+ reasonably fast instruction. */
+#define MOVE_MAX 2
+
+/* MOVE_RATIO is the number of move instructions that is better than a
+ block move. Make this small on 6811, since the code size grows very
+ large with each move. */
+#define MOVE_RATIO(speed) 3
+
+/* Define if shifts truncate the shift count which implies one can omit
+ a sign-extension or zero-extension of a shift count. */
+#define SHIFT_COUNT_TRUNCATED 1
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+ is done just by pretending it is already truncated. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* Specify the machine mode that pointers have. After generation of rtl, the
+ compiler makes no further distinction between pointers and any other
+ objects of this machine mode. */
+#define Pmode HImode
+
+/* A function address in a call instruction is a byte address (for indexing
+ purposes) so give the MEM rtx a byte's mode. */
+#define FUNCTION_MODE QImode
+
+extern int debug_m6811;
+extern int z_replacement_completed;
+extern int current_function_interrupt;
+extern int current_function_trap;
+extern int current_function_far;
+
+extern GTY(()) rtx m68hc11_compare_op0;
+extern GTY(()) rtx m68hc11_compare_op1;
+extern GTY(()) rtx m68hc11_soft_tmp_reg;
+extern GTY(()) rtx ix_reg;
+extern GTY(()) rtx iy_reg;
+extern GTY(()) rtx d_reg;
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11.md b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.md
new file mode 100644
index 000000000..0d40278ce
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.md
@@ -0,0 +1,7730 @@
+;;- 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_expand "tstsi"
+ [(set (cc0)
+ (match_operand:SI 0 "tst_operand" ""))]
+ ""
+ "
+{
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = const0_rtx;
+ DONE;
+}")
+
+(define_expand "tsthi"
+ [(set (cc0)
+ (match_operand:HI 0 "tst_operand" ""))]
+ ""
+ "
+{
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = const0_rtx;
+ DONE;
+}")
+
+(define_insn "tsthi_1"
+ [(set (cc0)
+ (match_operand:HI 0 "tst_operand" "dx,*y"))]
+ ""
+ "*
+{
+ if (D_REG_P (operands[0]) && !TARGET_M6812)
+ return \"std\\t%t0\";
+ else
+ return \"cp%0\\t#0\";
+}")
+
+(define_expand "tstqi"
+ [(set (cc0)
+ (match_operand:QI 0 "tst_operand" ""))]
+ ""
+ "
+{
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = const0_rtx;
+ DONE;
+}")
+
+;;
+;; Split pattern for (tst:QI) on an address register.
+;;
+(define_split
+ [(set (cc0)
+ (match_operand:QI 0 "hard_addr_reg_operand" ""))]
+ "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) (reg:QI D_REGNUM))
+ (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)
+ (match_operand:QI 0 "tst_operand" "m,d,*A,!u"))]
+ ""
+ "*
+{
+ 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)
+ (match_operand:QI 0 "tst_operand" "m"))
+ (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) (match_dup 0))
+ (set (match_dup 1) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
+ "operands[2] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
+
+
+;;--------------------------------------------------------------------
+;;- Compare
+;;--------------------------------------------------------------------
+
+(define_expand "cmpsi"
+ [(set (cc0)
+ (compare (match_operand:SI 0 "tst_operand" "")
+ (match_operand:SI 1 "cmp_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (SImode, operands[0]);
+
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = operands[1];
+ DONE;
+}")
+
+;;
+;; 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_expand "cmphi"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "tst_operand" "")
+ (match_operand:HI 1 "cmp_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (HImode, operands[0]);
+
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = operands[1];
+ DONE;
+}")
+
+(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_expand "cmpqi"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "tst_operand" "")
+ (match_operand:QI 1 "cmp_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (QImode, operands[0]);
+
+ m68hc11_compare_op0 = operands[0];
+ m68hc11_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_insn "bitcmpqi"
+ [(set (cc0)
+ (and:QI (match_operand:QI 0 "tst_operand" "d,d,d,m,!u")
+ (match_operand:QI 1 "cmp_operand" "im,*B,u,d,d")))]
+ ""
+ "@
+ bitb\\t%b1
+ #
+ bitb\\t%b1
+ bitb\\t%b0
+ bitb\\t%b0")
+
+(define_split /* "bitcmpqi" */
+ [(set (cc0)
+ (and:QI (match_operand:QI 0 "tst_operand" "")
+ (match_operand:QI 1 "hard_addr_reg_operand" "")))]
+ "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)
+ (and:QI (match_operand:QI 0 "tst_operand" "d,m")
+ (match_operand:QI 1 "cmp_operand" "m,d")))
+ (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)
+ (and:HI (match_operand:HI 0 "tst_operand" "d")
+ (match_operand:HI 1 "const_int_operand" "i")))]
+ "(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)
+ (zero_extract (match_operand:HI 0 "tst_operand" "d")
+ (match_operand:HI 1 "const_int_operand" "i")
+ (match_operand:HI 2 "const_int_operand" "i")))]
+ "(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 "beq"
+ [(set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (EQ, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bne"
+ [(set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (NE, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bgt"
+ [(set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (GT, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bgtu"
+ [(set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (GTU, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "blt"
+ [(set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (LT, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bltu"
+ [(set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (LTU, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bge"
+ [(set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (GE, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bgeu"
+ [(set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (GEU, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "ble"
+ [(set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (LE, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "bleu"
+ [(set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ m68hc11_expand_compare_and_branch (LEU, m68hc11_compare_op0,
+ m68hc11_compare_op1,
+ operands[0]);
+ 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)
+ (match_operand:QI 2 "memory_operand" ""))]
+ "TARGET_AUTO_INC_DEC
+ && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
+ && reg_mentioned_p (operands[0], operands[2])"
+ [(set (cc0) (match_dup 3))]
+ "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) (match_dup 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) (match_operand:HI 5 "hard_reg_operand" ""))]
+ "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\";
+}")
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc11.opt b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.opt
new file mode 100644
index 000000000..f0f29f2a7
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc11.opt
@@ -0,0 +1,94 @@
+; Options for the Motorola 68HC11 and 68HC12 port of the compiler.
+
+; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+;
+; 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/>.
+
+m6811
+Target RejectNegative InverseMask(M6812, M6811)
+Compile for a 68HC11
+
+m6812
+Target RejectNegative Mask(M6812)
+Compile for a 68HC12
+
+m68hc11
+Target RejectNegative InverseMask(M6812)
+Compile for a 68HC11
+
+m68hc12
+Target RejectNegative Mask(M6812) MaskExists
+Compile for a 68HC12
+
+; At the moment, there is no difference between the code generated
+; for -m68hc12 and -m68hcs12.
+m68hcs12
+Target RejectNegative Mask(M6812) MaskExists
+Compile for a 68HCS12
+
+m68s12
+Target RejectNegative Mask(M6812) MaskExists
+Compile for a 68HCS12
+
+mauto-incdec
+Target RejectNegative Report Mask(AUTO_INC_DEC)
+Auto pre/post decrement increment allowed
+
+minmax
+Target RejectNegative Report Mask(MIN_MAX)
+Min/max instructions allowed
+
+mlong-calls
+Target RejectNegative Report Mask(LONG_CALLS)
+Use call and rtc for function calls and returns
+
+mnoauto-incdec
+Target RejectNegative Report InverseMask(AUTO_INC_DEC)
+Auto pre/post decrement increment not allowed
+
+mnolong-calls
+Target RejectNegative Report InverseMask(LONG_CALLS)
+Use jsr and rts for function calls and returns
+
+mnominmax
+Target RejectNegative Report InverseMask(MIN_MAX)
+Min/max instructions not allowed
+
+mnorelax
+Target RejectNegative Report InverseMask(NO_DIRECT_MODE)
+Use direct addressing mode for soft registers
+
+mnoshort
+Target RejectNegative Report InverseMask(SHORT)
+Compile with 32-bit integer mode
+
+; Currently ignored.
+mreg-alloc=
+Target RejectNegative Joined
+Specify the register allocation order
+
+mrelax
+Target RejectNegative Report Mask(NO_DIRECT_MODE)
+Do not use direct addressing mode for soft registers
+
+mshort
+Target RejectNegative Report Mask(SHORT)
+Compile with 16-bit integer mode
+
+msoft-reg-count=
+Target RejectNegative Joined UInteger Var(m68hc11_soft_reg_count) Init(-1)
+Indicate the number of soft registers available
diff --git a/gcc-4.4.0/gcc/config/m68hc11/m68hc12.h b/gcc-4.4.0/gcc/config/m68hc11/m68hc12.h
new file mode 100644
index 000000000..22bdc008c
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/m68hc12.h
@@ -0,0 +1,45 @@
+/* Definitions of target machine for GNU compiler, for m68hc12.
+ Copyright (C) 1999, 2000, 2001, 2003, 2007 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/>. */
+
+/* Compile and assemble for a 68hc12 unless there is a -m68hc11 option. */
+#define ASM_SPEC \
+"%{m68hc11:-m68hc11}" \
+"%{m68hcs12:-m68hcs12}" \
+"%{!m68hc11:%{!m68hcs12:-m68hc12}}"
+#define LIB_SPEC ""
+#define CC1_SPEC ""
+
+/* We need to tell the linker the target elf format. Just pass an
+ emulation option. This can be overridden by -Wl option of gcc. */
+#define LINK_SPEC \
+"%{m68hc11:-m m68hc11elf}" \
+"%{m68hcs12:-m m68hc12elf}" \
+"%{!m68hc11:%{!m68hcs12:-m m68hc11elf}} %{mrelax:-relax}"
+
+#define CPP_SPEC \
+"%{mshort:-D__HAVE_SHORT_INT__ -D__INT__=16}\
+ %{!mshort:-D__INT__=32}\
+ %{m68hc11:-Dmc6811 -DMC6811 -Dmc68hc11}\
+ %{!m68hc11:%{!m68hc12:-Dmc6812 -DMC6812 -Dmc68hc12}}\
+ %{m68hcs12:-Dmc6812 -DMC6812 -Dmc68hcs12}\
+ %{fshort-double:-D__HAVE_SHORT_DOUBLE__}"
+
+/* Default target_flags if no switches specified. */
+#define TARGET_DEFAULT (MASK_M6812)
diff --git a/gcc-4.4.0/gcc/config/m68hc11/predicates.md b/gcc-4.4.0/gcc/config/m68hc11/predicates.md
new file mode 100644
index 000000000..72a32bdd9
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/predicates.md
@@ -0,0 +1,228 @@
+;; Predicate definitions for Motorola 68HC11 and 68HC12.
+;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+;;
+;; 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/>.
+
+;; TODO: Add a comment here.
+
+(define_predicate "stack_register_operand"
+ (match_code "subreg,reg")
+{
+ return SP_REG_P (op);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "d_register_operand"
+ (match_code "subreg,reg")
+{
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = XEXP (op, 0);
+
+ return GET_CODE (op) == REG
+ && (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || REGNO (op) == HARD_D_REGNUM
+ || (mode == QImode && REGNO (op) == HARD_B_REGNUM));
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "hard_addr_reg_operand"
+ (match_code "subreg,reg")
+{
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = XEXP (op, 0);
+
+ return GET_CODE (op) == REG
+ && (REGNO (op) == HARD_X_REGNUM
+ || REGNO (op) == HARD_Y_REGNUM
+ || REGNO (op) == HARD_Z_REGNUM);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "hard_reg_operand"
+ (match_code "subreg,reg")
+{
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = XEXP (op, 0);
+
+ return GET_CODE (op) == REG
+ && (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || H_REGNO_P (REGNO (op)));
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "m68hc11_logical_operator"
+ (match_code "and,ior,xor")
+{
+ return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "m68hc11_arith_operator"
+ (match_code "and,ior,xor,plus,minus,ashift,ashiftrt,lshiftrt,rotate,rotatert")
+{
+ return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
+ || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
+ || GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
+ || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
+ || GET_CODE (op) == ROTATERT;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "m68hc11_non_shift_operator"
+ (match_code "and,ior,xor,plus,minus")
+{
+ return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
+ || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "m68hc11_unary_operator"
+ (match_code "neg,not,sign_extend,zero_extend")
+{
+ return GET_CODE (op) == NEG || GET_CODE (op) == NOT
+ || GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
+})
+
+;; Return true if op is a shift operator.
+
+(define_predicate "m68hc11_shift_operator"
+ (match_code "ashift,ashiftrt,lshiftrt,rotate,rotatert")
+{
+ return GET_CODE (op) == ROTATE || GET_CODE (op) == ROTATERT
+ || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFT
+ || GET_CODE (op) == ASHIFTRT;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "m68hc11_eq_compare_operator"
+ (match_code "eq,ne")
+{
+ return GET_CODE (op) == EQ || GET_CODE (op) == NE;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "non_push_operand"
+ (match_code "subreg,reg,mem")
+{
+ if (general_operand (op, mode) == 0)
+ return 0;
+
+ if (push_operand (op, mode) == 1)
+ return 0;
+ return 1;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "splitable_operand"
+ (match_code "subreg,reg,mem,symbol_ref,label_ref,const_int,const_double")
+{
+ if (general_operand (op, mode) == 0)
+ return 0;
+
+ if (push_operand (op, mode) == 1)
+ return 0;
+
+ /* Reject a (MEM (MEM X)) because the patterns that use non_push_operand
+ need to split such addresses to access the low and high part but it
+ is not possible to express a valid address for the low part. */
+ if (mode != QImode && GET_CODE (op) == MEM
+ && GET_CODE (XEXP (op, 0)) == MEM)
+ return 0;
+ return 1;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "reg_or_some_mem_operand"
+ (match_code "subreg,reg,mem")
+{
+ if (GET_CODE (op) == MEM)
+ {
+ rtx op0 = XEXP (op, 0);
+ int addr_mode;
+
+ if (symbolic_memory_operand (op0, mode))
+ return 1;
+
+ if (IS_STACK_PUSH (op))
+ return 1;
+
+ if (GET_CODE (op) == REG && reload_in_progress
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_memory_loc[REGNO (op)])
+ {
+ op = reg_equiv_memory_loc[REGNO (op)];
+ op = eliminate_regs (op, 0, NULL_RTX);
+ }
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ op0 = XEXP (op, 0);
+ addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
+ addr_mode &= ~ADDR_INDIRECT;
+ return m68hc11_valid_addressing_p (op0, mode, addr_mode);
+ }
+
+ return register_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "tst_operand"
+ (match_code "subreg,reg,mem")
+{
+ if (GET_CODE (op) == MEM && reload_completed == 0)
+ {
+ rtx addr = XEXP (op, 0);
+ if (m68hc11_auto_inc_p (addr))
+ return 0;
+ }
+ return nonimmediate_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "cmp_operand"
+ (match_code "subreg,reg,mem,symbol_ref,label_ref,const_int,const_double")
+{
+ if (GET_CODE (op) == MEM)
+ {
+ rtx addr = XEXP (op, 0);
+ if (m68hc11_auto_inc_p (addr))
+ return 0;
+ }
+ return general_operand (op, mode);
+})
diff --git a/gcc-4.4.0/gcc/config/m68hc11/t-m68hc11 b/gcc-4.4.0/gcc/config/m68hc11/t-m68hc11
new file mode 100644
index 000000000..7978641f9
--- /dev/null
+++ b/gcc-4.4.0/gcc/config/m68hc11/t-m68hc11
@@ -0,0 +1,77 @@
+RANLIB_FOR_TARGET = ` \
+ if [ -f $(objdir)/../binutils/ranlib ] ; then \
+ echo $(objdir)/../binutils/ranlib ; \
+ else \
+ if [ "$(host)" = "$(target)" ] ; then \
+ echo ranlib; \
+ else \
+ if [ -f $(bindir)/$(target_noncanonical)-ranlib ] ; then \
+ echo $(bindir)/$(target_noncanonical)-ranlib ; \
+ else \
+ t='$(program_transform_cross_name)'; echo ranlib | sed -e $$t ; \
+ fi; \
+ fi; \
+ fi`
+
+LIB1ASMSRC = m68hc11/larith.asm
+LIB1ASMFUNCS = _mulsi3 \
+ _mulqi3 _ashlsi3 _ashrsi3 _lshrsi3 \
+ _divmodhi4 _mulhi3 _mulhi32 \
+ _memcpy _memset _negsi2 _one_cmplsi2 \
+ _regs_min _regs_frame _regs_d1_2 \
+ _regs_d3_4 _regs_d5_6 _regs_d7_8 _regs_d9_16 _regs_d17_32 \
+ _premain __exit _abort _cleanup \
+ _adddi3 _subdi3 _notdi2 _rotlhi3 _rotrhi3 \
+ _ashrhi3 _lshrhi3 _lshlhi3 _ashrqi3 _lshlqi3 _map_data _init_bss \
+ _ctor _dtor _far_tramp _call_far _return_far
+
+TARGET_LIBGCC2_CFLAGS = -DUSE_GAS -DIN_GCC -Dinhibit_libc
+
+# C implementation of 32-bit div/mod.
+LIB2FUNCS_EXTRA = $(srcdir)/config/udivmodsi4.c \
+ $(srcdir)/config/divmod.c $(srcdir)/config/udivmod.c
+
+# Don't compile with -g1 this reduces the size of some sections (.eh_frame).
+LIBGCC2_DEBUG_CFLAGS =-g
+LIBGCC2_CFLAGS = -Os -mrelax $(LIBGCC2_INCLUDES) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2
+
+MULTILIB_OPTIONS = m68hc11/m68hc12 mshort fshort-double
+MULTILIB_DIRNAMES =
+MULTILIB_MATCHES = m68hc11=m6811 m68hc12=m6812 m68hc12=m68hcs12
+MULTILIB_EXCEPTIONS = -mnoshort -mno68hc11
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define SMALL_MACHINE' >> dp-bit.c
+ echo '#define CMPtype HItype' >> dp-bit.c
+ echo '#ifdef __LITTLE_ENDIAN__' >> dp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >>dp-bit.c
+ echo '#endif' >> dp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ echo '#define CMPtype HItype' >> fp-bit.c
+ echo '#define SMALL_MACHINE' >> fp-bit.c
+ echo '#ifdef __LITTLE_ENDIAN__' >> fp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >>fp-bit.c
+ echo '#endif' >> fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+CRT0_S = $(srcdir)/config/m68hc11/m68hc11-crt0.S
+MCRT0_S= $(srcdir)/config/m68hc11/m68hc11-crt0.S
+
+CRT0STUFF_T_CFLAGS =
+
+# Assemble startup files.
+$(T)crt1.o: $(CRT0_S) $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1.o -x assembler-with-cpp $(CRT0_S)
+
+EXTRA_MULTILIB_PARTS = crt1.o