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