OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/m68hc11
    from Rev 154 to Rev 816
    Reverse comparison

Rev 154 → Rev 816

/t-m68hc11-gas
0,0 → 1,79
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`
 
T_CPPFLAGS = -DUSE_GAS
 
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
/m68hc11-protos.h
0,0 → 1,130
/* Prototypes for exported functions defined in m68hc11.c
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
Contributed by Stephane Carrez (stcarrez@nerim.fr)
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
 
extern int m68hc11_override_options (void);
extern int m68hc11_optimization_options (int,int);
extern void m68hc11_conditional_register_usage (void);
extern int hard_regno_mode_ok (int, enum machine_mode);
extern int m68hc11_hard_regno_rename_ok (int, int);
 
extern int m68hc11_total_frame_size (void);
extern int m68hc11_initial_frame_pointer_offset (void);
extern int m68hc11_initial_elimination_offset (int, int);
 
extern void expand_prologue (void);
extern void expand_epilogue (void);
 
#ifdef TREE_CODE
extern void m68hc11_function_arg_advance (CUMULATIVE_ARGS*,
enum machine_mode,
tree,
int);
#endif
 
#ifdef RTX_CODE
extern int m68hc11_auto_inc_p (rtx);
 
extern void m68hc11_initialize_trampoline (rtx, rtx, rtx);
 
extern rtx m68hc11_expand_compare_and_branch (enum rtx_code, rtx, rtx, rtx);
extern enum reg_class preferred_reload_class (rtx, enum reg_class);
 
extern int m68hc11_go_if_legitimate_address (rtx, enum machine_mode, int);
 
extern int m68hc11_legitimize_address (rtx*, rtx, enum machine_mode);
 
extern void m68hc11_notice_update_cc (rtx, rtx);
extern void m68hc11_notice_keep_cc (rtx);
 
extern void m68hc11_gen_movqi (rtx, rtx*);
extern void m68hc11_gen_movhi (rtx, rtx*);
extern void m68hc11_gen_rotate (enum rtx_code, rtx, rtx*);
 
extern void m68hc11_output_swap (rtx, rtx*);
 
extern int next_insn_test_reg (rtx, rtx);
 
extern void print_operand (FILE*, rtx, int);
extern void print_operand_address (FILE*, rtx);
 
extern int m68hc11_reload_operands (rtx*);
 
extern int dead_register_here (rtx, rtx);
 
extern int push_pop_operand_p (rtx);
extern void m68hc11_split_move (rtx, rtx, rtx);
extern void m68hc11_split_compare_and_branch (enum rtx_code,
rtx, rtx, rtx);
 
extern rtx m68hc11_gen_lowpart (enum machine_mode, rtx);
extern rtx m68hc11_gen_highpart (enum machine_mode, rtx);
 
#ifdef HAVE_MACHINE_MODES
extern int m68hc11_memory_move_cost (enum machine_mode, enum reg_class, int);
extern int m68hc11_register_move_cost (enum machine_mode,
enum reg_class, enum reg_class);
 
extern void m68hc11_emit_libcall (const char*, enum rtx_code,
enum machine_mode, enum machine_mode,
int, rtx*);
extern int m68hc11_small_indexed_indirect_p (rtx, enum machine_mode);
extern int m68hc11_symbolic_p (rtx, enum machine_mode);
extern int m68hc11_indirect_p (rtx, enum machine_mode);
extern int go_if_legitimate_address2 (rtx, enum machine_mode, int);
 
extern int reg_or_indexed_operand (rtx,enum machine_mode);
extern int memory_indexed_operand (rtx, enum machine_mode);
 
extern void m68hc11_split_logical (enum machine_mode, int, rtx*);
 
extern int m68hc11_register_indirect_p (rtx, enum machine_mode);
extern int m68hc11_valid_addressing_p (rtx, enum machine_mode, int);
 
extern int symbolic_memory_operand (rtx, enum machine_mode);
 
extern int memory_reload_operand (rtx, enum machine_mode);
extern int arith_src_operand (rtx, enum machine_mode);
extern int soft_reg_operand (rtx, enum machine_mode);
 
#if defined TREE_CODE
extern void m68hc11_init_cumulative_args (CUMULATIVE_ARGS*, tree, rtx);
 
extern rtx m68hc11_function_arg (const CUMULATIVE_ARGS* ,
enum machine_mode,
tree, int);
extern int m68hc11_function_arg_padding (enum machine_mode, tree);
 
extern void m68hc11_function_epilogue (FILE*,int);
 
extern int m68hc11_is_far_symbol (rtx);
extern int m68hc11_is_trap_symbol (rtx);
extern int m68hc11_page0_symbol_p (rtx x);
 
#endif /* TREE_CODE */
 
extern HOST_WIDE_INT m68hc11_min_offset;
extern HOST_WIDE_INT m68hc11_max_offset;
extern int m68hc11_addr_mode;
 
#endif /* HAVE_MACHINE_MODES */
#endif /* RTX_CODE */
 
/larith.asm
0,0 → 1,1345
/* libgcc routines for M68HC11 & M68HC12.
Copyright (C) 1999, 2000, 2001, 2002, 2003 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 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file. (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)
 
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.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
 
.file "larith.asm"
 
#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
;-----------------------------------------
/predicates.md
0,0 → 1,228
;; Predicate definitions for Motorola 68HC11 and 68HC12.
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; TODO: Add a comment here.
 
(define_predicate "stack_register_operand"
(match_code "subreg,reg")
{
return SP_REG_P (op);
})
 
;; TODO: Add a comment here.
 
(define_predicate "d_register_operand"
(match_code "subreg,reg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
 
if (GET_CODE (op) == SUBREG)
op = XEXP (op, 0);
 
return GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO (op) == HARD_D_REGNUM
|| (mode == QImode && REGNO (op) == HARD_B_REGNUM));
})
 
;; TODO: Add a comment here.
 
(define_predicate "hard_addr_reg_operand"
(match_code "subreg,reg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
 
if (GET_CODE (op) == SUBREG)
op = XEXP (op, 0);
 
return GET_CODE (op) == REG
&& (REGNO (op) == HARD_X_REGNUM
|| REGNO (op) == HARD_Y_REGNUM
|| REGNO (op) == HARD_Z_REGNUM);
})
 
;; TODO: Add a comment here.
 
(define_predicate "hard_reg_operand"
(match_code "subreg,reg")
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
 
if (GET_CODE (op) == SUBREG)
op = XEXP (op, 0);
 
return GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| H_REGNO_P (REGNO (op)));
})
 
;; TODO: Add a comment here.
 
(define_predicate "m68hc11_logical_operator"
(match_code "and,ior,xor")
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
})
 
;; TODO: Add a comment here.
 
(define_predicate "m68hc11_arith_operator"
(match_code "and,ior,xor,plus,minus,ashift,ashiftrt,lshiftrt,rotate,rotatert")
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
|| GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
|| GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
|| GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
|| GET_CODE (op) == ROTATERT;
})
 
;; TODO: Add a comment here.
 
(define_predicate "m68hc11_non_shift_operator"
(match_code "and,ior,xor,plus,minus")
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
|| GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
})
 
;; TODO: Add a comment here.
 
(define_predicate "m68hc11_unary_operator"
(match_code "neg,not,sign_extend,zero_extend")
{
return GET_CODE (op) == NEG || GET_CODE (op) == NOT
|| GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
})
 
;; Return true if op is a shift operator.
 
(define_predicate "m68hc11_shift_operator"
(match_code "ashift,ashiftrt,lshiftrt,rotate,rotatert")
{
return GET_CODE (op) == ROTATE || GET_CODE (op) == ROTATERT
|| GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFT
|| GET_CODE (op) == ASHIFTRT;
})
 
;; TODO: Add a comment here.
 
(define_predicate "m68hc11_eq_compare_operator"
(match_code "eq,ne")
{
return GET_CODE (op) == EQ || GET_CODE (op) == NE;
})
 
;; TODO: Add a comment here.
 
(define_predicate "non_push_operand"
(match_code "subreg,reg,mem")
{
if (general_operand (op, mode) == 0)
return 0;
 
if (push_operand (op, mode) == 1)
return 0;
return 1;
})
 
;; TODO: Add a comment here.
 
(define_predicate "splitable_operand"
(match_code "subreg,reg,mem,symbol_ref,label_ref,const_int,const_double")
{
if (general_operand (op, mode) == 0)
return 0;
 
if (push_operand (op, mode) == 1)
return 0;
 
/* Reject a (MEM (MEM X)) because the patterns that use non_push_operand
need to split such addresses to access the low and high part but it
is not possible to express a valid address for the low part. */
if (mode != QImode && GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == MEM)
return 0;
return 1;
})
 
;; TODO: Add a comment here.
 
(define_predicate "reg_or_some_mem_operand"
(match_code "subreg,reg,mem")
{
if (GET_CODE (op) == MEM)
{
rtx op0 = XEXP (op, 0);
int addr_mode;
 
if (symbolic_memory_operand (op0, mode))
return 1;
 
if (IS_STACK_PUSH (op))
return 1;
 
if (GET_CODE (op) == REG && reload_in_progress
&& REGNO (op) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (op)])
{
op = reg_equiv_memory_loc[REGNO (op)];
op = eliminate_regs (op, 0, NULL_RTX);
}
if (GET_CODE (op) != MEM)
return 0;
 
op0 = XEXP (op, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
addr_mode &= ~ADDR_INDIRECT;
return m68hc11_valid_addressing_p (op0, mode, addr_mode);
}
 
return register_operand (op, mode);
})
 
;; TODO: Add a comment here.
 
(define_predicate "tst_operand"
(match_code "subreg,reg,mem")
{
if (GET_CODE (op) == MEM && reload_completed == 0)
{
rtx addr = XEXP (op, 0);
if (m68hc11_auto_inc_p (addr))
return 0;
}
return nonimmediate_operand (op, mode);
})
 
;; TODO: Add a comment here.
 
(define_predicate "cmp_operand"
(match_code "subreg,reg,mem,symbol_ref,label_ref,const_int,const_double")
{
if (GET_CODE (op) == MEM)
{
rtx addr = XEXP (op, 0);
if (m68hc11_auto_inc_p (addr))
return 0;
}
return general_operand (op, mode);
})
/m68hc11.md
0,0 → 1,7730
;;- Machine description file for Motorola 68HC11 and 68HC12.
;;- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
;;- Free Software Foundation, Inc.
;;- Contributed by Stephane Carrez (stcarrez@nerim.fr)
 
;; This file is part of GCC.
 
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
 
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
 
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; Note:
;; A first 68HC11 port was made by Otto Lind (otto@coactive.com)
;; on gcc 2.6.3. I have used it as a starting point for this port.
;; However, this new port is a complete re-write. Its internal
;; design is completely different. The generated code is not
;; compatible with the gcc 2.6.3 port.
;;
;; The gcc 2.6.3 port is available at:
;;
;; ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
;;
 
;;- Instruction patterns. When multiple patterns apply,
;;- the first one in the file is chosen.
;;-
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
;;-
;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
;;- updates for most instructions.
 
;;
;; The following constraints are used:
;;
;; Single pair registers:
;; a register 'a' 8-bit
;; b register 'b' 8-bit
;; d register 'd' 16-bit
;; t pseudo soft register 'TMP' 16-bit
;; v register 'd' for 68hc11, 16-bit
;; NO_REG for 68hc12
;; (used for scratch register)
;; w register 'sp' 16-bit
;; x register 'x' 16-bit
;; y register 'y' 16-bit
;; z register 'z' 16-bit (fake r for 68HC11 and 68HC12)
;; D register 'd+x' 32-bit
;;
;; Group of registers:
;; q register 'a' or 'b' or 'd' 8-bit
;; u pseudo soft register 16-bit
;; A register 'x', 'y', 'z' 16-bit
;; B register 'x', 'y' 16-bit
;; h register 'd', 'x', 'y', 'z' 16-bit
;;
;; Other constraints:
;;
;; Q an operand which is in memory but whose address is constant
;; (i.e., a (MEM (SYMBOL_REF x))). This constraint is used by
;; bset/bclr instructions together with linker relaxation. The
;; operand can be translated to a page0 addressing mode if the
;; symbol address is in page0 (0..255).
;;
;; R an operand which is in memory and whose address is expressed
;; with 68HC11/68HC12 indexed addressing mode. In general this
;; is any valid (MEM) except a (MEM (SYMBOL_REF x)).
;;
;; U an operand which is in memory and if it uses the 68HC12 indexed
;; addressing mode, the offset is in the range -16..+15. This is
;; used by 68HC12 movb/movw instructions since they do not accept
;; the full 16-bit offset range (as other insn do).
;;
;;
;; Immediate integer operand constraints:
;; `L' is for range -65536 to 65536
;; `M' is for values whose 16-bit low part is 0
;; 'N' is for +1 or -1.
;; 'O' is for 16 (for rotate using swap).
;; 'P' is for range -8 to 2 (used by addhi_sp)
;;
;; In many cases, it's not possible to use the 'g' or 'r' constraints.
;;
;; Operands modifiers:
;;
;; %b Get the low part of the operand (to obtain a QImode)
;; This modifier must always be used for QImode operations
;; because a correction must be applied when the operand
;; is a soft register (ex: *ZD1). Otherwise, we generate
;; *ZD1 and this is the high part of the register. For other
;; kinds of operands, if the operand is already QImode, no
;; additional correction is made.
;; %h Get the high part of the operand (to obtain a QImode)
;; %t Represents the temporary/scratch register *_.tmp
;; The scratch register is used in some cases when GCC puts
;; some values in bad registers.
;;
;; 32/64-bit Patterns:
;; The 68HC11 does not support 32/64-bit operations. Most of the
;; 32/64-bit patterns are defined to split the instruction in
;; 16-bits patterns. Providing split patterns generates better code
;; than letting GCC implement the 32/64-bit operation itself.
;;
;;
;; Notes:
;;
;; o For iorqi3, andqi3, xorqi3 patterns, we must accept the 'A' constraint
;; otherwise some insn are not satisfied.
;;
;; o Split patterns that create a swap_areg pattern (xgdx or xgdy) must
;; be valid only when z_replacement_completed == 2 because once these
;; swap instructions are generated, a flow/cse pass fails to handle
;; them correctly (it would treat the X, Y or D register as dead sometimes).
;;
;; o Some split pattern generate instructions that operate on 'a' or 'b'
;; register directly (high part and low part of D respectively).
;; Such split pattern must also be valid when z_replacement_completed == 2
;; because flow/cse is not aware that D is composed of {a, b}.
;;
;; o Split patterns that generate a (mem:QI (symbol_reg _.dx)) to access
;; the high part of a soft register must be expanded after z_replacement
;; pass.
;;
;;---------------------------------------------------------------------------
;; Constants
 
(define_constants [
;; Register numbers
(X_REGNUM 0) ; Index X register
(D_REGNUM 1) ; Data register
(Y_REGNUM 2) ; Index Y register
(SP_REGNUM 3) ; Stack pointer
(PC_REGNUM 4) ; Program counter
(A_REGNUM 5) ; A (high part of D)
(B_REGNUM 6) ; B (low part of D)
(CC_REGNUM 7) ; Condition code register
(SOFT_TMP_REGNUM 10) ; TMP soft register
(SOFT_Z_REGNUM 11) ; Z soft register
(SOFT_XY_REGNUM 12) ; XY soft register
])
 
(include "predicates.md")
 
;;--------------------------------------------------------------------
;;- Test
;;--------------------------------------------------------------------
;;
;; The test and compare insn must not accept a memory operand with
;; an auto-inc mode. If we do this, the reload can emit move insns
;; after the test or compare. Such move will set the flags and therefore
;; break the comparison. This can happen if the auto-inc register
;; does not happen to be a hard register (i.e., reloading occurs).
;; An offsetable memory operand should be ok. The 'tst_operand' and
;; 'cmp_operand' predicates take care of this rule.
;;
(define_expand "tstsi"
[(set (cc0)
(match_operand:SI 0 "tst_operand" ""))]
""
"
{
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = const0_rtx;
DONE;
}")
 
(define_expand "tsthi"
[(set (cc0)
(match_operand:HI 0 "tst_operand" ""))]
""
"
{
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = const0_rtx;
DONE;
}")
 
(define_insn "tsthi_1"
[(set (cc0)
(match_operand:HI 0 "tst_operand" "dx,*y"))]
""
"*
{
if (D_REG_P (operands[0]) && !TARGET_M6812)
return \"std\\t%t0\";
else
return \"cp%0\\t#0\";
}")
 
(define_expand "tstqi"
[(set (cc0)
(match_operand:QI 0 "tst_operand" ""))]
""
"
{
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = const0_rtx;
DONE;
}")
 
;;
;; Split pattern for (tst:QI) on an address register.
;;
(define_split
[(set (cc0)
(match_operand:QI 0 "hard_addr_reg_operand" ""))]
"z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 1))
(set (match_dup 1) (reg:HI D_REGNUM))])
(set (cc0) (reg:QI D_REGNUM))
(parallel [(set (reg:HI D_REGNUM) (match_dup 1))
(set (match_dup 1) (reg:HI D_REGNUM))])]
"operands[1] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
(define_insn "tstqi_1"
[(set (cc0)
(match_operand:QI 0 "tst_operand" "m,d,*A,!u"))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
else if (D_REG_P (operands[0]))
return \"tstb\";
 
else if (dead_register_here (insn, d_reg))
return \"ldab\\t%b0\";
 
else
return \"tst\\t%b0\";
}")
 
;;
;; tstqi_z_used, cmpqi_z_used and cmphi_z_used are patterns generated
;; during the Z register replacement. They are used when an operand
;; uses the Z register as an index register (i.e., (MEM:QI (REG:HI Z))).
;; In that case, we have to preserve the values of the replacement
;; register (as well as the CC0 since the insns are compare insns).
;; To do this, the replacement register is pushed on the stack and
;; restored after the real compare. A pattern+split is defined to
;; avoid problems with the flow+cse register pass which are made
;; after Z register replacement.
;;
(define_insn_and_split "tstqi_z_used"
[(set (cc0)
(match_operand:QI 0 "tst_operand" "m"))
(use (match_operand:HI 1 "hard_reg_operand" "dxy"))
(use (reg:HI SOFT_Z_REGNUM))]
""
"#"
"z_replacement_completed == 2"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 1))
(set (match_dup 1) (match_dup 2))
(set (cc0) (match_dup 0))
(set (match_dup 1) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
"operands[2] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
 
 
;;--------------------------------------------------------------------
;;- Compare
;;--------------------------------------------------------------------
 
(define_expand "cmpsi"
[(set (cc0)
(compare (match_operand:SI 0 "tst_operand" "")
(match_operand:SI 1 "cmp_operand" "")))]
""
"
{
if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
operands[0] = force_reg (SImode, operands[0]);
 
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = operands[1];
DONE;
}")
 
;;
;; Comparison of a hard register with another one is provided because
;; it helps GCC to avoid to spill a pseudo hard register.
;; We use a temporary in page 0, this is equivalent to a pseudo hard reg.
;; (except that we loose the information that the value is saved in it).
;;
;; The split pattern transforms the comparison into a save of one hard
;; register and a comparison with the temporary.
;;
(define_split
[(set (cc0)
(compare (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "hard_reg_operand" "")))]
"TARGET_M6811
&& reload_completed && !(Z_REG_P (operands[0]) || Z_REG_P (operands[1]))"
[(set (match_dup 2) (match_dup 1))
(set (cc0)
(compare (match_dup 0) (match_dup 2)))]
"operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);")
 
(define_split
[(set (cc0)
(compare (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "hard_reg_operand" "")))]
"0 && TARGET_M6812
&& reload_completed && !(Z_REG_P (operands[0]) || Z_REG_P (operands[1]))"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 1))
(set (cc0)
(compare (match_dup 0) (mem:HI (post_inc:HI (reg:HI SP_REGNUM)))))]
"")
 
(define_expand "cmphi"
[(set (cc0)
(compare (match_operand:HI 0 "tst_operand" "")
(match_operand:HI 1 "cmp_operand" "")))]
""
"
{
if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
operands[0] = force_reg (HImode, operands[0]);
 
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = operands[1];
DONE;
}")
 
(define_insn "cmphi_1_hc12"
[(set (cc0)
(compare (match_operand:HI 0 "tst_operand"
"d,?xy,xyd,?xy,d,m,!u,dxy,dxy")
(match_operand:HI 1 "general_operand"
"i,i,!u,m,m,dxy,dxy,?*d*A,!*w")))]
"TARGET_M6812"
"*
{
if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
{
cc_status.flags |= CC_REVERSED;
return \"cp%1\\t%0\";
}
else if (SP_REG_P (operands[1]))
return \"sts\\t2,-sp\n\\tcp%0\\t2,sp+\";
else if (H_REG_P (operands[1]))
return \"psh%1\n\\tcp%0\\t2,sp+\";
else
return \"cp%0\\t%1\";
}")
 
(define_insn "cmphi_1_hc11"
[(set (cc0)
(compare (match_operand:HI 0 "tst_operand"
"dx,y,xyd,?xy,d,m,m,dxy,dxy,?u*z,dxy,*z")
(match_operand:HI 1 "cmp_operand"
"i,i,!u,m,m,?xy,d,?*d*A,?u,dxy,!*w,i")))]
"TARGET_M6811"
"*
{
if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
{
cc_status.flags |= CC_REVERSED;
return \"cp%1\\t%0\";
}
else if (H_REG_P (operands[1]))
return \"#\";
else
return \"cp%0\\t%1\";
}")
 
(define_insn_and_split "cmphi_z_used"
[(set (cc0)
(compare (match_operand:HI 0 "tst_operand" "dxy,m")
(match_operand:HI 1 "cmp_operand" "mi,dxy")))
(use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
(use (reg:HI SOFT_Z_REGNUM))]
""
"#"
"z_replacement_completed == 2"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
(set (match_dup 2) (match_dup 3))
(set (cc0) (compare (match_dup 0) (match_dup 1)))
(set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
"operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
 
;;
;; 8-bit comparison with address register.
;; There is no such comparison instruction, we have to temporarily switch
;; the address register and the D register and do the comparison with D.
;; The xgdx and xgdy instructions preserve the flags.
;;
(define_split
[(set (cc0)
(compare (match_operand:QI 0 "hard_addr_reg_operand" "")
(match_operand:QI 1 "cmp_operand" "")))]
"z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])
(set (cc0)
(compare (reg:QI D_REGNUM) (match_dup 1)))
(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
(define_split
[(set (cc0)
(compare (match_operand:QI 0 "hard_reg_operand" "")
(match_operand:QI 1 "hard_reg_operand" "")))]
"reload_completed"
[(set (match_dup 3) (match_dup 4))
(set (cc0)
(compare (match_dup 0) (match_dup 2)))]
"operands[2] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
operands[3] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[4] = gen_rtx_REG (HImode, REGNO (operands[1]));")
 
(define_expand "cmpqi"
[(set (cc0)
(compare (match_operand:QI 0 "tst_operand" "")
(match_operand:QI 1 "cmp_operand" "")))]
""
"
{
if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
operands[0] = force_reg (QImode, operands[0]);
 
m68hc11_compare_op0 = operands[0];
m68hc11_compare_op1 = operands[1];
DONE;
}")
 
(define_insn "bitcmpqi"
[(set (cc0)
(and:QI (match_operand:QI 0 "tst_operand" "d,d,d,m,!u")
(match_operand:QI 1 "cmp_operand" "im,*B,u,d,d")))]
""
"@
bitb\\t%b1
#
bitb\\t%b1
bitb\\t%b0
bitb\\t%b0")
 
(define_split /* "bitcmpqi" */
[(set (cc0)
(and:QI (match_operand:QI 0 "tst_operand" "")
(match_operand:QI 1 "hard_addr_reg_operand" "")))]
"z_replacement_completed == 2"
[(set (match_dup 3) (match_dup 2))
(set (cc0) (and:QI (match_dup 0) (match_dup 4)))]
"operands[2] = gen_rtx_REG (HImode, REGNO (operands[1]));
operands[3] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[4] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);")
 
(define_insn_and_split "bitcmpqi_z_used"
[(set (cc0)
(and:QI (match_operand:QI 0 "tst_operand" "d,m")
(match_operand:QI 1 "cmp_operand" "m,d")))
(use (match_operand:HI 2 "hard_reg_operand" "xy,xy"))
(use (reg:HI SOFT_Z_REGNUM))]
""
"#"
"z_replacement_completed == 2"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
(set (match_dup 2) (match_dup 3))
(set (cc0) (and:QI (match_dup 0) (match_dup 1)))
(set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
"operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
 
(define_insn "bitcmphi"
[(set (cc0)
(and:HI (match_operand:HI 0 "tst_operand" "d")
(match_operand:HI 1 "const_int_operand" "i")))]
"(INTVAL (operands[1]) & 0x0ff) == 0
|| (INTVAL (operands[1]) & 0x0ff00) == 0"
"*
{
if ((INTVAL (operands[1]) & 0x0ff) == 0)
return \"bita\\t%h1\";
else
return \"bitb\\t%1\";
}")
 
(define_insn "bitcmpqi_12"
[(set (cc0)
(zero_extract (match_operand:HI 0 "tst_operand" "d")
(match_operand:HI 1 "const_int_operand" "i")
(match_operand:HI 2 "const_int_operand" "i")))]
"(unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 8
|| (((unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 16)
&& (unsigned) INTVAL (operands[2]) >= 8)"
"*
{
rtx ops[1];
int mask;
int startpos = INTVAL (operands[2]);
int bitsize = INTVAL (operands[1]);
 
if (startpos >= 8)
{
startpos -= 8;
mask = (1 << (startpos + bitsize)) - 1;
mask &= ~((1 << startpos) - 1);
 
ops[0] = GEN_INT (mask);
output_asm_insn (\"bita\\t%0\", ops);
}
else
{
mask = (1 << (startpos + bitsize)) - 1;
mask &= ~((1 << startpos) - 1);
 
ops[0] = GEN_INT (mask);
output_asm_insn (\"bitb\\t%0\", ops);
}
return \"\";
}")
 
(define_insn "cmpqi_1"
[(set (cc0)
(compare (match_operand:QI 0 "tst_operand" "d,m,d,!u,*B,d*B")
(match_operand:QI 1 "cmp_operand" "im,d,!u,d,dim*A,*u")))]
""
"*
{
if (A_REG_P (operands[0]) || A_REG_P (operands[1]))
{
return \"#\";
}
else if (D_REG_P (operands[0]))
{
return \"cmpb\\t%b1\";
}
cc_status.flags |= CC_REVERSED;
return \"cmpb\\t%b0\";
}")
 
(define_insn_and_split "cmpqi_z_used"
[(set (cc0)
(compare (match_operand:QI 0 "tst_operand" "dxy,m")
(match_operand:QI 1 "cmp_operand" "m,dxy")))
(use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
(use (reg:HI SOFT_Z_REGNUM))]
""
"#"
"z_replacement_completed == 2"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
(set (match_dup 2) (match_dup 3))
(set (cc0) (compare (match_dup 0) (match_dup 1)))
(set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
"operands[3] = gen_rtx_REG (HImode, SOFT_Z_REGNUM);")
 
;;--------------------------------------------------------------------
;;- Move strict_low_part
;;--------------------------------------------------------------------
;;
;; The (strict_low_part ...) patterns are replaced by normal (set) patterns.
;; The replacement must be made at the very end because we loose the
;; (strict_low_part ...) information. This is correct for our machine
;; description but not for GCC optimization passes.
;;
(define_insn_and_split "movstrictsi"
[(set (strict_low_part (match_operand:SI 0 "non_push_operand" "+um,D,D"))
(match_operand:SI 1 "general_operand" "D,Dim,uD"))]
""
"#"
"z_replacement_completed == 2"
[(set (match_dup 0) (match_dup 1))]
"")
 
(define_insn_and_split "movstricthi"
[(set (strict_low_part (match_operand:HI 0 "non_push_operand" "+um,dA,dA"))
(match_operand:HI 1 "general_operand" "dA,dAim,u"))]
""
"#"
"z_replacement_completed == 2"
[(set (match_dup 0) (match_dup 1))]
"")
 
(define_insn_and_split "movstrictqi"
[(set (strict_low_part (match_operand:QI 0 "non_push_operand" "+mu,!dA"))
(match_operand:QI 1 "general_operand" "d,imudA"))]
""
"#"
"z_replacement_completed == 2"
[(set (match_dup 0) (match_dup 1))]
"")
 
;;--------------------------------------------------------------------
;;- 64-bit Move Operations.
;; The movdi and movdf patterns are identical except for the mode.
;; They are also very similar to those for movsi and movsf.
;;
;; For 68HC11, we need a scratch register (either D, X, Y)
;; because there is no memory->memory moves. It must be defined with
;; earlyclobber (&) so that it does not appear in the source or destination
;; address. Providing patterns for movdi/movdf allows GCC to generate
;; better code. [Until now, the scratch register is limited to D because
;; otherwise we can run out of registers in the A_REGS class for reload].
;;
;; For 68HC12, the scratch register is not necessary. To use the same
;; pattern and same split, we use the 'v' constraint. This tells the
;; reload to use the _.tmp register (which is not used at all).
;; The insn will be split in one or several memory moves (movw).
;; [SCz: this does not work ?? So, I switched temporary to 'd' reg]
;;--------------------------------------------------------------------
(define_expand "movdi"
[(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
(match_operand:DI 1 "general_operand" ""))
(clobber (match_scratch:HI 2 ""))])]
""
"
/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_movdi_internal (operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
")
 
;; Separate push from normal moves to avoid reloading problems
;; The 'clr' is not able to push on 68HC11 so we really need a scratch.
;; We can also accept more scratch registers.
(define_insn_and_split "*pushdi_internal"
[(set (match_operand:DI 0 "push_operand" "=<,<,<,<")
(match_operand:DI 1 "general_operand" "i,U,m,!u"))
(clobber (match_scratch:HI 2 "=&dA,&d,&d,&dA"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_insn_and_split "movdi_internal"
[(set (match_operand:DI 0 "non_push_operand" "=m!u,U,!u,U,m,m,!u")
(match_operand:DI 1 "general_operand" "K,iU,iU,!u,mi,!u,!mu"))
(clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_expand "movdf"
[(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
(match_operand:DF 1 "general_operand" ""))
(clobber (match_scratch:HI 2 ""))])]
""
"/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_movdf_internal (operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
")
 
;; See pushdi_internal
(define_insn_and_split "*pushdf_internal"
[(set (match_operand:DF 0 "push_operand" "=<,<,<,<")
(match_operand:DF 1 "general_operand" "i,U,m,!u"))
(clobber (match_scratch:HI 2 "=&dA,&d,&d,&dA"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_insn_and_split "movdf_internal"
[(set (match_operand:DF 0 "non_push_operand" "=mu,U,m,!u,U,m,!u")
(match_operand:DF 1 "general_operand" "G,iU,mi,iU,!u,!u,!mu"))
(clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
;;--------------------------------------------------------------------
;;- 32-bit Move Operations.
;; The movsi and movsf patterns are identical except for the mode.
;; When we move to/from a hard register (d+x), we don't need a scratch.
;; Otherwise, a scratch register is used as intermediate register for
;; the move. The '&' constraint is necessary to make sure the reload
;; pass does not give us a register that dies in the insn and is used
;; for input/output operands.
;;--------------------------------------------------------------------
(define_expand "movsi"
[(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
(match_operand:SI 1 "general_operand" ""))
(clobber (match_scratch:HI 2 ""))])]
""
"/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_movsi_internal (operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
")
 
(define_insn_and_split "*pushsi_internal"
[(set (match_operand:SI 0 "push_operand" "=<,<,<,<,<")
(match_operand:SI 1 "general_operand" "!D,i,U,m,!u"))
(clobber (match_scratch:HI 2 "=X,&dA,&d,&d,&dA"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_insn_and_split "movsi_internal"
[(set (match_operand:SI 0 "nonimmediate_operand" "=mu,mu,?D,m,?D,?u,?u,!u,D")
(match_operand:SI 1 "general_operand" "K,imu,im,?D,!u,?D,mi,!u,!D"))
(clobber (match_scratch:HI 2 "=X,&d,X,X,X,X,&d,&d,X"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_expand "movsf"
[(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
(match_operand:SF 1 "general_operand" ""))
(clobber (match_scratch:HI 2 ""))])]
""
"/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_movsf_internal (operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
")
 
(define_insn_and_split "*pushsf_internal"
[(set (match_operand:SF 0 "push_operand" "=<,<,<,<,<")
(match_operand:SF 1 "general_operand" "!D,i,U,m,!u"))
(clobber (match_scratch:HI 2 "=X,&dA,&d,&d,&dA"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
(define_insn_and_split "movsf_internal"
[(set (match_operand:SF 0 "nonimmediate_operand" "=m!u,m,D,m,D,!u,!u,!u,D")
(match_operand:SF 1 "general_operand" "G,im,im,D,!u,D,mi,!u,!D"))
(clobber (match_scratch:HI 2 "=X,&d,X,X,X,X,&d,&d,X"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (operands[0], operands[1], operands[2]);
DONE;")
 
 
;;--------------------------------------------------------------------
;;- 16-bit Move Operations.
;; We don't need a scratch register.
;;--------------------------------------------------------------------
 
(define_insn "*movhi2_push"
[(set (match_operand:HI 0 "push_operand" "=<,<,<")
(match_operand:HI 1 "general_operand" "xy,?d,!z"))]
"TARGET_M6811 && !TARGET_M6812"
"*
{
cc_status = cc_prev_status;
if (D_REG_P (operands[1]))
{
output_asm_insn (\"pshb\", operands);
return \"psha\";
}
else if (X_REG_P (operands[1]))
{
return \"pshx\";
}
else if (Y_REG_P (operands[1]))
{
return \"pshy\";
}
fatal_insn (\"Invalid register in the instruction\", insn);
}")
 
(define_insn "*movhi2_pop"
[(set (match_operand:HI 0 "nonimmediate_operand" "=xy,d")
(match_operand:HI 1 "pop_operand" ">,>"))]
"TARGET_M6811"
"*
{
cc_status = cc_prev_status;
if (D_REG_P (operands[0]))
{
output_asm_insn (\"pula\", operands);
return \"pulb\";
}
else if (X_REG_P (operands[0]))
{
return \"pulx\";
}
else if (Y_REG_P (operands[0]))
{
return \"puly\";
}
fatal_insn (\"Invalid register in the instruction\", insn);
}")
 
(define_expand "movhi"
[(set (match_operand:HI 0 "nonimmediate_operand" "")
(match_operand:HI 1 "general_operand" ""))]
""
"
{
if (reload_in_progress)
{
if (m68hc11_reload_operands (operands))
{
DONE;
}
}
if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
{
if (GET_CODE (operands[0]) == MEM &&
(GET_CODE (operands[1]) == MEM
|| GET_CODE (operands[1]) == CONST_INT))
{
operands[1] = force_reg (HImode, operands[1]);
}
else if (IS_STACK_PUSH (operands[0])
&& GET_CODE (operands[1]) != REG)
{
operands[1] = force_reg (HImode, operands[1]);
}
}
/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
}")
 
(define_insn "*movhi_68hc12"
[(set (match_operand:HI 0 "nonimmediate_operand" "=U,dAw,dAw,m,U,U,m,!u")
(match_operand:HI 1 "general_operand" "U,dAwim,!u,K,dAwi,!u,dAw,riU"))]
"TARGET_M6812"
"*
{
m68hc11_gen_movhi (insn, operands);
return \"\";
}")
 
(define_insn "movhi_const0"
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,A,um")
(const_int 0))]
"TARGET_M6811"
"@
clra\\n\\tclrb
ld%0\\t#0
clr\\t%b0\\n\\tclr\\t%h0")
 
(define_insn "*movhi_m68hc11"
[(set (match_operand:HI 0 "nonimmediate_operand" "=dAw,!u,m,m,dAw,!*u")
(match_operand:HI 1 "general_operand" "dAwim,dAw,dA,?Aw,!*u,dAw"))]
"TARGET_M6811"
"*
{
m68hc11_gen_movhi (insn, operands);
return \"\";
}")
 
;;--------------------------------------------------------------------
;;- 8-bit Move Operations.
;; We don't need a scratch register.
;;--------------------------------------------------------------------
;;
;; The *a alternative also clears the high part of the register.
;; This should be ok since this is not the (strict_low_part) set.
;;
(define_insn "movqi_const0"
[(set (match_operand:QI 0 "non_push_operand" "=d,m,!u,*A,!*q")
(const_int 0))]
""
"@
clrb
clr\\t%b0
clr\\t%b0
ld%0\\t#0
clr%0")
 
;;
;; 8-bit operations on address registers.
;;
;; Switch temporary to the D register and load the value in B.
;; This is possible as long as the address register does not
;; appear in the source operand.
;;
(define_split
[(set (match_operand:QI 0 "hard_addr_reg_operand" "")
(match_operand:QI 1 "general_operand" ""))]
"z_replacement_completed == 2
&& !reg_mentioned_p (operands[0], operands[1])
&& !(D_REG_P (operands[1]) || Q_REG_P (operands[1]))"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
(set (match_dup 2) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (match_dup 1))
(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
(set (match_dup 2) (reg:HI D_REGNUM))])]
"operands[2] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
;;
;; 8-bit operations on address registers.
;;
(define_split
[(set (match_operand:QI 0 "nonimmediate_operand" "")
(match_operand:QI 1 "hard_addr_reg_operand" ""))]
"z_replacement_completed == 2
&& !reg_mentioned_p (operands[1], operands[0])
&& !(D_REG_P (operands[0]) || Q_REG_P (operands[0]))"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
(set (match_dup 2) (reg:HI D_REGNUM))])
(set (match_dup 0) (reg:QI D_REGNUM))
(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
(set (match_dup 2) (reg:HI D_REGNUM))])]
"operands[2] = gen_rtx_REG (HImode, REGNO (operands[1]));")
 
(define_insn "*movqi2_push"
[(set (match_operand:QI 0 "push_operand" "=<,<")
(match_operand:QI 1 "general_operand" "d,!*A"))]
""
"*
{
if (A_REG_P (operands[1]))
return \"#\";
 
cc_status = cc_prev_status;
return \"pshb\";
}")
 
 
(define_expand "movqi"
[(set (match_operand:QI 0 "nonimmediate_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"
{
if (reload_in_progress)
{
if (m68hc11_reload_operands (operands))
{
DONE;
}
}
if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
{
if (GET_CODE (operands[0]) == MEM
&& (GET_CODE (operands[1]) == MEM
|| GET_CODE (operands[1]) == CONST_INT))
{
operands[1] = force_reg (QImode, operands[1]);
}
else if (IS_STACK_PUSH (operands[0])
&& GET_CODE (operands[1]) != REG)
{
operands[1] = force_reg (QImode, operands[1]);
}
}
/* For push/pop, emit a REG_INC note to make sure the reload
inheritance and reload CSE pass notice the change of the stack
pointer. */
if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
{
rtx insn;
 
insn = emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
stack_pointer_rtx,
REG_NOTES (insn));
DONE;
}
}")
 
(define_insn "*movqi_68hc12"
[(set (match_operand:QI 0 "nonimmediate_operand"
"=U,d*AU*q,d*A*qU,d*A*q,m,?*u,m")
(match_operand:QI 1 "general_operand"
"U,*ri*q,U,m,d*q,*ri*qU,!*A"))]
"TARGET_M6812"
"*
{
m68hc11_gen_movqi (insn, operands);
return \"\";
}")
 
(define_insn "*movqi_m68hc11"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d*A*q,m,m,d*A*q,*u")
(match_operand:QI 1 "general_operand" "d*Aim*q,d*q,!*A,*u,d*A*q"))]
"TARGET_M6811"
"*
{
m68hc11_gen_movqi (insn, operands);
return \"\";
}")
 
;;--------------------------------------------------------------------
;;- Swap registers
;;--------------------------------------------------------------------
;; Swapping registers is used for split patterns.
(define_insn "swap_areg"
[(set (match_operand:HI 0 "hard_reg_operand" "=d,A")
(match_operand:HI 1 "hard_reg_operand" "=A,d"))
(set (match_dup 1) (match_dup 0))]
""
"*
{
m68hc11_output_swap (insn, operands);
return \"\";
}")
 
;;--------------------------------------------------------------------
;;- Truncation insns.
;;--------------------------------------------------------------------
;;
;; Truncation are not necessary because GCC knows how to truncate,
;; specially when values lie in consecutive registers.
;;
 
(define_expand "floatunssisf2"
[(set (match_operand:SF 0 "nonimmediate_operand" "")
(unsigned_float:SF (match_operand:SI 1 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"__floatunsisf\", UNSIGNED_FLOAT,
SFmode, SImode, 2, operands);
DONE;")
 
(define_expand "floatunssidf2"
[(set (match_operand:DF 0 "nonimmediate_operand" "")
(unsigned_float:DF (match_operand:SI 1 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"__floatunsidf\", UNSIGNED_FLOAT,
DFmode, SImode, 2, operands);
DONE;")
 
;;--------------------------------------------------------------------
;;- Zero extension insns.
;;--------------------------------------------------------------------
 
;;
;; 64-bit extend. The insn will be split into 16-bit instructions just
;; before the final pass. We need a scratch register for the split.
;; The final value can be generated on the stack directly. This is more
;; efficient and useful for conversions made during parameter passing rules.
;;
(define_insn "zero_extendqidi2"
[(set (match_operand:DI 0 "nonimmediate_operand" "=m,!u,m,!u")
(zero_extend:DI
(match_operand:QI 1 "nonimmediate_operand" "m,dmu,*B,*B")))
(clobber (match_scratch:HI 2 "=&d,&dB,&d,&dB"))]
""
"#")
 
(define_split
[(set (match_operand:DI 0 "push_operand" "")
(zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
(clobber (match_scratch:HI 2 "=&dB"))]
"z_replacement_completed == 2"
[(const_int 0)]
"
{
rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
rtx push = m68hc11_gen_lowpart (HImode, low);
rtx src = operands[1];
 
/* Source operand must be in a hard register. */
if (!H_REG_P (src))
{
src = gen_rtx_REG (QImode, REGNO (operands[2]));
emit_move_insn (src, operands[1]);
}
 
/* Source is in D, we can push B then one word of 0 and we do
a correction on the stack pointer. */
if (D_REG_P (src))
{
emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
emit_move_insn (operands[2], const0_rtx);
if (D_REG_P (operands[2]))
{
emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
}
else
{
emit_move_insn (push, operands[2]);
emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
gen_rtx_REG (HImode, HARD_SP_REGNUM),
const1_rtx));
}
}
else
{
/* Source is in X or Y. It's better to push the 16-bit register
and then to some stack adjustment. */
src = gen_rtx_REG (HImode, REGNO (src));
emit_move_insn (push, src);
emit_move_insn (operands[2], const0_rtx);
emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
gen_rtx_REG (HImode, HARD_SP_REGNUM),
const1_rtx));
emit_move_insn (push, operands[2]);
emit_insn (gen_addhi3 (gen_rtx_REG (HImode, HARD_SP_REGNUM),
gen_rtx_REG (HImode, HARD_SP_REGNUM),
const1_rtx));
}
emit_move_insn (push, operands[2]);
emit_move_insn (push, operands[2]);
emit_move_insn (push, operands[2]);
DONE;
}")
 
(define_split
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
(clobber (match_scratch:HI 2 "=&dB"))]
"z_replacement_completed == 2"
[(const_int 0)]
"
{
rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
rtx low2 = m68hc11_gen_lowpart (HImode, low);
rtx src = operands[1];
 
/* Source operand must be in a hard register. */
if (!H_REG_P (src))
{
src = gen_rtx_REG (QImode, REGNO (operands[2]));
emit_move_insn (src, operands[1]);
}
 
emit_move_insn (m68hc11_gen_lowpart (QImode, low2), src);
emit_move_insn (operands[2], const0_rtx);
src = gen_rtx_REG (QImode, REGNO (operands[2]));
emit_move_insn (m68hc11_gen_highpart (QImode, low2), src);
 
emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
low = m68hc11_gen_highpart (SImode, operands[0]);
emit_move_insn (m68hc11_gen_lowpart (HImode, low), operands[2]);
emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
DONE;
}")
 
(define_insn "zero_extendhidi2"
[(set (match_operand:DI 0 "non_push_operand" "=m,m,m,m,!u,!u")
(zero_extend:DI
(match_operand:HI 1 "nonimmediate_operand" "m,d,A,!u,dmA,!u")))
(clobber (match_scratch:HI 2 "=&d,&B,&d,&dB,&dB,&dB"))]
""
"#")
 
(define_split
[(set (match_operand:DI 0 "non_push_operand" "")
(zero_extend:DI
(match_operand:HI 1 "nonimmediate_operand" "")))
(clobber (match_scratch:HI 2 ""))]
"z_replacement_completed == 2"
[(const_int 0)]
"
{
rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
rtx high = m68hc11_gen_highpart (SImode, operands[0]);
rtx src = operands[1];
 
/* Make sure the source is in a hard register. */
if (!H_REG_P (src))
{
src = operands[2];
emit_move_insn (src, operands[1]);
}
 
/* Move the low part first for the push. */
emit_move_insn (m68hc11_gen_lowpart (HImode, low), src);
 
/* Now, use the scratch register to fill in the zeros. */
emit_move_insn (operands[2], const0_rtx);
emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
DONE;
}")
 
(define_insn "zero_extendsidi2"
[(set (match_operand:DI 0 "nonimmediate_operand" "=m,m,!u,!u")
(zero_extend:DI
(match_operand:SI 1 "nonimmediate_operand" "m,Du,m,Du")))
(clobber (match_scratch:HI 2 "=d,d,d,d"))]
""
"#")
 
(define_split
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(zero_extend:DI
(match_operand:SI 1 "nonimmediate_operand" "")))
(clobber (match_scratch:HI 2 ""))]
"z_replacement_completed == 2"
[(const_int 0)]
"
{
rtx low = m68hc11_gen_lowpart (SImode, operands[0]);
rtx high = m68hc11_gen_highpart (SImode, operands[0]);
 
/* Move the low part first so that this is ok for a push. */
m68hc11_split_move (low, operands[1], operands[2]);
 
/* Use the scratch register to clear the high part of the destination. */
emit_move_insn (operands[2], const0_rtx);
emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
DONE;
}")
 
;;
;; For 16->32bit unsigned extension, we don't allow generation on the stack
;; because it's less efficient.
;;
(define_insn "zero_extendhisi2"
[(set (match_operand:SI 0 "non_push_operand" "=D,m,u,m,m,!u,!u")
(zero_extend:SI
(match_operand:HI 1 "nonimmediate_operand" "dAmu,dA,dA,m,!u,m,!u")))
(clobber (match_scratch:HI 2 "=X,X,X,&d,&dB,&dB,&dB"))]
""
"#")
 
(define_split
[(set (match_operand:SI 0 "non_push_operand" "")
(zero_extend:SI
(match_operand:HI 1 "nonimmediate_operand" "")))
(clobber (match_scratch:HI 2 ""))]
"reload_completed"
[(const_int 0)]
"
{
rtx src = operands[1];
 
if (!H_REG_P (src) && !H_REG_P (operands[0]))
{
src = operands[2];
emit_move_insn (src, operands[1]);
}
emit_move_insn (m68hc11_gen_lowpart (HImode, operands[0]), src);
emit_move_insn (m68hc11_gen_highpart (HImode, operands[0]), const0_rtx);
DONE;
}")
 
(define_insn "zero_extendqisi2"
[(set (match_operand:SI 0 "non_push_operand" "=D,D,m,m,u")
(zero_extend:SI
(match_operand:QI 1 "nonimmediate_operand" "dmu,xy,d,xy,dxy")))]
""
"#")
 
(define_split
[(set (match_operand:SI 0 "non_push_operand" "")
(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
"reload_completed && !X_REG_P (operands[0])"
[(set (match_dup 2) (zero_extend:HI (match_dup 1)))
(set (match_dup 3) (const_int 0))]
"
operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")
 
(define_split
[(set (match_operand:SI 0 "hard_reg_operand" "")
(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
"z_replacement_completed == 2 && X_REG_P (operands[0])"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 4) (const_int 0))
(set (match_dup 5) (zero_extend:HI (match_dup 6)))]
"
if (X_REG_P (operands[1]))
{
emit_insn (gen_swap_areg (gen_rtx_REG (HImode, HARD_D_REGNUM),
gen_rtx_REG (HImode, HARD_X_REGNUM)));
emit_insn (gen_zero_extendqihi2 (gen_rtx_REG (HImode, HARD_D_REGNUM),
gen_rtx_REG (QImode, HARD_D_REGNUM)));
emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM),
const0_rtx);
DONE;
}
 
if (reg_mentioned_p (gen_rtx_REG (HImode, HARD_X_REGNUM), operands[1]))
{
emit_insn (gen_zero_extendqihi2 (m68hc11_gen_lowpart (HImode,
operands[0]),
operands[1]));
emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM), const0_rtx);
DONE;
}
operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
operands[5] = m68hc11_gen_lowpart (HImode, operands[0]);
if (A_REG_P (operands[1]))
{
operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[3] = gen_rtx_REG (HImode, REGNO (operands[1]));
operands[6] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
}
else
{
operands[5] = operands[2] =
operands[3] = gen_rtx_REG (HImode, HARD_D_REGNUM);
operands[6] = operands[1];
}
")
 
(define_insn "zero_extendqihi2"
[(set (match_operand:HI 0 "non_push_operand" "=dm,d,*A,!*u,d,m,!*u")
(zero_extend:HI
(match_operand:QI 1 "nonimmediate_operand" "d,*A,d*Am,d,!um,*A,*A")))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
if (H_REG_P (operands[0]))
{
output_asm_insn (\"clra\", operands);
if (operands[0] != operands[1]
&& !(D_REG_P (operands[0]) && D_REG_P (operands[1])))
{
if (X_REG_P (operands[1])
|| (D_REG_P (operands[1]) && X_REG_P (operands[0])))
{
output_asm_insn (\"stx\\t%t1\", operands);
output_asm_insn (\"ldab\\t%T0\", operands);
}
else if (Y_REG_P (operands[1])
|| (D_REG_P (operands[1]) && Y_REG_P (operands[0])))
{
output_asm_insn (\"sty\\t%t1\", operands);
output_asm_insn (\"ldab\\t%T0\", operands);
}
else
{
output_asm_insn (\"ldab\\t%b1\", operands);
}
cc_status.flags |= CC_NOT_NEGATIVE;
}
else
{
/* Status refers to the clra insn. Status is ok for others
* since we have loaded the value in B.
*/
CC_STATUS_INIT;
}
return \"\";
}
 
if (A_REG_P (operands[1]))
{
output_asm_insn (\"st%1\\t%0\", operands);
output_asm_insn (\"clr\\t%h0\", operands);
CC_STATUS_INIT;
}
else
{
output_asm_insn (\"clr\\t%h0\", operands);
output_asm_insn (\"stab\\t%b0\", operands);
cc_status.flags |= CC_NOT_NEGATIVE;
}
 
return \"\";
}")
 
 
;;--------------------------------------------------------------------
;;- Sign extension insns.
;;--------------------------------------------------------------------
 
(define_insn "extendqisi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,u")
(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dmux,d,d")))]
""
"*
{
rtx ops[3];
int need_tst = 0;
 
/* The 68HC12 has a sign-extension instruction. Use it when the
destination is the register (X,D). First sign-extend the low
part and fill X with the sign-extension of the high part. */
if (TARGET_M6812 && X_REG_P (operands[0]))
{
if (!D_REG_P (operands[1]))
{
ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movqi (insn, ops);
}
return \"sex\\tb,d\\n\\tsex\\ta,x\";
}
 
ops[2] = gen_label_rtx ();
 
if (X_REG_P (operands[1]))
{
output_asm_insn (\"xgdx\", operands);
need_tst = 1;
}
else if (X_REG_P (operands[0]))
{
/* X can be used as an indexed addressing in the source.
Get the value before clearing it. */
if (reg_mentioned_p (ix_reg, operands[1]))
{
output_asm_insn (\"ldab\\t%b1\", operands);
need_tst = 1;
}
output_asm_insn (\"ldx\\t#0\", operands);
}
 
output_asm_insn (\"clra\", operands);
if (!X_REG_P (operands[0]))
{
ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
ops[1] = m68hc11_gen_lowpart (QImode, ops[0]);
 
if (IS_STACK_PUSH (operands[0]))
{
output_asm_insn (\"pshb\", ops);
output_asm_insn (\"tstb\", ops);
}
else
{
output_asm_insn (\"stab\\t%b1\", ops);
}
}
else if (D_REG_P (operands[1]) || need_tst)
{
output_asm_insn (\"tstb\", operands);
}
else
{
output_asm_insn (\"ldab\\t%b1\", operands);
}
output_asm_insn (\"bpl\\t%l2\", ops);
output_asm_insn (\"deca\", operands);
if (X_REG_P (operands[0]))
output_asm_insn (\"dex\", operands);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
 
if (!X_REG_P (operands[0]))
{
if (IS_STACK_PUSH (operands[0]))
{
output_asm_insn (\"psha\", ops);
output_asm_insn (\"psha\", ops);
output_asm_insn (\"psha\", ops);
}
else
{
output_asm_insn (\"staa\\t%h0\", ops);
 
ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
if (dead_register_here (insn, d_reg))
{
output_asm_insn (\"tab\", ops);
output_asm_insn (\"std\\t%0\", ops);
}
else
{
output_asm_insn (\"staa\\t%b0\", ops);
output_asm_insn (\"staa\\t%h0\", ops);
}
}
}
 
CC_STATUS_INIT;
return \"\";
}")
 
 
(define_insn "extendqihi2"
[(set (match_operand:HI 0 "non_push_operand" "=d,*x*ym,u")
(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "dum,0,0")))]
""
"*
{
rtx ops[2];
 
if (A_REG_P (operands[0]))
return \"#\";
 
ops[0] = gen_label_rtx ();
if (D_REG_P (operands[0]))
{
if (TARGET_M6812)
{
if (!D_REG_P (operands[1]))
{
ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movqi (insn, ops);
}
return \"sex\\tb,d\";
}
output_asm_insn (\"clra\", operands);
if (H_REG_P (operands[1]))
{
output_asm_insn (\"tstb\", operands);
}
else
{
output_asm_insn (\"ldab\\t%b1\", operands);
}
output_asm_insn (\"bpl\\t%l0\", ops);
output_asm_insn (\"deca\", operands);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
}
else
{
output_asm_insn (\"clr\\t%h0\", operands);
if (m68hc11_register_indirect_p (operands[1], HImode))
{
ops[1] = operands[1];
output_asm_insn (\"brclr\\t%b1 #0x80 %l0\", ops);
CC_STATUS_INIT;
}
else
{
output_asm_insn (\"tst\\t%b1\", operands);
output_asm_insn (\"bpl\\t%l0\", ops);
}
output_asm_insn (\"dec\\t%h0\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
}
 
return \"\";
}")
 
;;
;; Split the special case where the source of the sign extend is
;; either Y or Z. In that case, we can't move the source in the D
;; register directly. The movhi pattern handles this move by using
;; a temporary scratch memory location.
;;
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
"reload_completed && (Y_REG_P (operands[1]) || Z_REG_P (operands[1]))"
[(set (reg:HI D_REGNUM) (match_dup 1))
(set (match_dup 0) (sign_extend:SI (reg:HI D_REGNUM)))]
"")
 
(define_insn "extendhisi2"
[(set (match_operand:SI 0 "register_operand" "=D,D,D")
(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "m,!r,dA")))]
""
"*
{
rtx ops[2];
int x_reg_used;
 
if (Y_REG_P (operands[1]))
return \"#\";
 
if (X_REG_P (operands[1]))
{
output_asm_insn (\"xgdx\", operands);
x_reg_used = 1;
}
else
{
/* X can be used as an indexed addressing in the source.
Get the value before clearing it. */
x_reg_used = reg_mentioned_p (ix_reg, operands[1]);
if (x_reg_used)
{
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
}
}
 
CC_STATUS_INIT;
if (TARGET_M6812 && 0)
{
/* This sequence of code is larger than the one for 68HC11.
Don't use it; keep it for documentation. */
if (!D_REG_P (operands[1]) && !x_reg_used)
{
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
}
output_asm_insn (\"sex\\ta,x\", operands);
output_asm_insn (\"xgdx\", operands);
output_asm_insn (\"sex\\ta,d\", operands);
return \"xgdx\";
}
 
output_asm_insn (\"ldx\\t#0\", operands);
if (D_REG_P (operands[1]) || x_reg_used)
{
output_asm_insn (\"tsta\", operands);
}
else
{
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
}
 
ops[0] = gen_label_rtx ();
output_asm_insn (\"bpl\\t%l0\", ops);
output_asm_insn (\"dex\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
 
return \"\";
}")
 
 
;;--------------------------------------------------------------------
;;- Min and Max instructions (68HC12).
;;--------------------------------------------------------------------
(define_insn "uminqi3"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
(umin:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
(match_operand:QI 2 "general_operand" "m,d")))]
"TARGET_M6812 && TARGET_MIN_MAX"
"*
{
/* Flags are set according to (sub:QI (operand 1) (operand2)).
The mina/minm use A as the source or destination. This is the
high part of D. There is no way to express that in the pattern
so we must use 'exg a,b' to put the operand in the good register. */
CC_STATUS_INIT;
if (D_REG_P (operands[0]))
{
return \"exg\\ta,b\\n\\tmina\\t%2\\n\\texg\\ta,b\";
}
else
{
return \"exg\\ta,b\\n\\tminm\\t%0\\n\\texg\\ta,b\";
}
}")
 
(define_insn "umaxqi3"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
(umax:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
(match_operand:QI 2 "general_operand" "m,d")))]
"TARGET_M6812 && TARGET_MIN_MAX"
"*
{
/* Flags are set according to (sub:QI (operand 1) (operand2)).
The maxa/maxm use A as the source or destination. This is the
high part of D. There is no way to express that in the pattern
so we must use 'exg a,b' to put the operand in the good register. */
CC_STATUS_INIT;
if (D_REG_P (operands[0]))
{
return \"exg\\ta,b\\n\\tmaxa\\t%2\\n\\texg\\ta,b\";
}
else
{
return \"exg\\ta,b\\n\\tmaxm\\t%0\\n\\texg\\ta,b\";
}
}")
 
(define_insn "uminhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
(umin:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
(match_operand:HI 2 "general_operand" "m,d")))]
"TARGET_M6812 && TARGET_MIN_MAX"
"*
{
/* Flags are set according to (sub:HI (operand 1) (operand2)). */
CC_STATUS_INIT;
if (D_REG_P (operands[0]))
{
return \"emind\\t%2\";
}
else
{
return \"eminm\\t%0\";
}
}")
 
(define_insn "umaxhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
(umax:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
(match_operand:HI 2 "general_operand" "m,d")))]
"TARGET_M6812 && TARGET_MIN_MAX"
"*
{
/* Flags are set according to (sub:HI (operand 1) (operand2)). */
CC_STATUS_INIT;
if (D_REG_P (operands[0]))
{
return \"emaxd\\t%2\";
}
else
{
return \"emaxm\\t%0\";
}
}")
 
 
;;--------------------------------------------------------------------
;;- Add instructions.
;;--------------------------------------------------------------------
;; 64-bit: Use a library call because what GCC generates is huge.
;;
(define_expand "adddi3"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(plus:DI (match_operand:DI 1 "general_operand" "")
(match_operand:DI 2 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"___adddi3\", PLUS, DImode, DImode, 3, operands);
DONE;")
 
;;
;; - 32-bit Add.
;;
(define_expand "addsi3"
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 ""))])]
""
"")
 
(define_insn "*addsi3_zero_extendhi"
[(set (match_operand:SI 0 "register_operand" "=D,D,D,D")
(plus:SI (zero_extend:SI
(match_operand:HI 1 "general_operand" "dxi,!u,mdxi,!u"))
(match_operand:SI 2 "general_operand" "mi,mi,D?u,!Du")))
(clobber (match_scratch:HI 3 "=X,X,X,X"))]
""
"*
{
rtx ops[3];
 
if (X_REG_P (operands[2]))
{
ops[0] = operands[1];
}
else
{
if (X_REG_P (operands[1]))
{
output_asm_insn (\"xgdx\", ops);
}
else if (!D_REG_P (operands[1]))
{
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
}
ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
}
ops[2] = gen_label_rtx ();
 
/* ldx preserves the carry, propagate it by incrementing X directly. */
output_asm_insn (\"addd\\t%0\", ops);
if (!X_REG_P (operands[2]))
output_asm_insn (\"ldx\\t%1\", ops);
 
output_asm_insn (\"bcc\\t%l2\", ops);
output_asm_insn (\"inx\", ops);
 
CC_STATUS_INIT;
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
return \"\";
}")
 
 
(define_split /* "*addsi3_zero_extendqi" */
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (zero_extend:SI
(match_operand:QI 1 "general_operand" ""))
(match_operand:SI 2 "memory_operand" "")))
(clobber (match_scratch:HI 3 "=X,X"))]
"reload_completed"
[(set (reg:HI D_REGNUM) (zero_extend:HI (match_dup 1)))
(parallel [(set (match_dup 0)
(plus:SI (zero_extend:SI (reg:HI D_REGNUM)) (match_dup 2)))
(clobber (match_dup 3))])]
"")
 
(define_insn "*addsi3_zero_extendqi"
[(set (match_operand:SI 0 "register_operand" "=D,D")
(plus:SI (zero_extend:SI
(match_operand:QI 1 "general_operand" "dAmi,!dAmiu"))
(match_operand:SI 2 "general_operand" "miD,!muiD")))
(clobber (match_scratch:HI 3 "=X,X"))]
""
"*
{
rtx ops[4];
 
if (GET_CODE (operands[2]) == MEM)
return \"#\";
 
if (X_REG_P (operands[2]))
{
if (H_REG_P (operands[1]))
{
ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
ops[1] = gen_rtx_REG (HImode, REGNO (operands[1]));
m68hc11_gen_movhi (insn, ops);
}
else
{
ops[0] = operands[1];
}
ops[1] = const0_rtx;
}
else
{
if (X_REG_P (operands[1]))
{
output_asm_insn (\"xgdx\", ops);
}
else if (!D_REG_P (operands[1]))
{
ops[0] = gen_rtx_REG (QImode, HARD_D_REGNUM);
ops[1] = operands[1];
m68hc11_gen_movqi (insn, ops);
}
 
ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
ops[1] = ops[0];
ops[2] = m68hc11_gen_highpart (HImode, operands[2]);
output_asm_insn (\"clra\", ops);
}
 
/* ldx preserves the carry, propagate it by incrementing X directly. */
output_asm_insn (\"addb\\t%b0\", ops);
output_asm_insn (\"adca\\t%h1\", ops);
if (!X_REG_P (operands[2]))
output_asm_insn (\"ldx\\t%2\", ops);
 
/* If the above adca was adding some constant, we don't need to propagate
the carry unless the constant was 0xff. */
if (X_REG_P (operands[2])
|| GET_CODE (ops[1]) != CONST_INT
|| ((INTVAL (ops[1]) & 0x0ff00) == 0x0ff00))
{
ops[3] = gen_label_rtx ();
 
output_asm_insn (\"bcc\\t%l3\", ops);
output_asm_insn (\"inx\", ops);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[3]));
}
CC_STATUS_INIT;
return \"\";
}")
 
(define_insn "*addsi3"
[(set (match_operand:SI 0 "non_push_operand" "=o,D,!u,?D,D")
(plus:SI (match_operand:SI 1 "non_push_operand" "%0,0,0,0,0")
(match_operand:SI 2 "general_operand" "ML,i,ML,?D,?oiu")))
(clobber (match_scratch:HI 3 "=d,X,d,X,X"))]
""
"*
{
rtx ops[3];
const char* add_insn;
const char* inc_insn;
const char* incb_mem;
const char* inch_mem;
HOST_WIDE_INT val;
 
if (which_alternative > 2)
{
return \"#\";
}
 
val = INTVAL (operands[2]);
if ((val & 0x0ffffL) == 0)
{
if (!H_REG_P (operands[0]))
{
ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
output_asm_insn (\"ldd\\t%0\", ops);
output_asm_insn (\"addd\\t%1\", ops);
output_asm_insn (\"std\\t%0\", ops);
return \"\";
}
else if (val == 1)
{
return \"inx\";
}
else
{
return \"#\";
}
}
if ((val & 0xffff0000L) != 0 && (val & 0xffff0000L) != 0xffff0000L)
{
return \"#\";
}
 
if (val >= 0)
{
ops[1] = operands[2];
add_insn = \"addd\\t%1\";
inc_insn = \"inx\\t\";
incb_mem = \"inc\\t%b1\";
inch_mem = \"inc\\t%h1\";
}
else
{
ops[1] = GEN_INT (- val);
add_insn = \"subd\\t%1\";
inc_insn = \"dex\";
incb_mem = \"dec\\t%b1\";
inch_mem = \"dec\\t%h1\";
}
ops[2] = gen_label_rtx ();
if (!H_REG_P (operands[0]))
{
ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
output_asm_insn (\"ldd\\t%0\", ops);
}
output_asm_insn (add_insn, ops);
if (!H_REG_P (operands[0]))
{
output_asm_insn (\"std\\t%0\", ops);
}
output_asm_insn (\"bcc\\t%l2\", ops);
if (H_REG_P (operands[0]))
{
output_asm_insn (inc_insn, ops);
}
else
{
ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
ops[1] = ops[0];
if (INTVAL (operands[2]) < 0)
{
output_asm_insn (\"ldd\\t%1\", ops);
output_asm_insn (\"addd\\t#-1\", ops);
output_asm_insn (\"std\\t%1\", ops);
}
else
{
output_asm_insn (incb_mem, ops);
output_asm_insn (\"bne\\t%l2\", ops);
output_asm_insn (inch_mem, ops);
}
}
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
 
CC_STATUS_INIT;
return \"\";
}")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "const_int_operand" "")))
(clobber (match_scratch:HI 3 ""))]
"reload_completed && z_replacement_completed == 2
&& ((INTVAL (operands[2]) & 0x0FFFF) == 0)"
[(set (match_dup 5) (match_dup 6))
(set (reg:HI 0) (plus:HI (reg:HI 0) (match_dup 4)))
(set (match_dup 6) (match_dup 5))]
"operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
if (X_REG_P (operands[0]))
{
operands[5] = operands[6] = gen_rtx_REG (HImode, HARD_D_REGNUM);
}
else
{
operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
operands[5] = operands[3];
}
")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 "=X"))]
"reload_completed && z_replacement_completed == 2
&& (GET_CODE (operands[2]) != CONST_INT ||
(!(INTVAL (operands[2]) >= -65536 && INTVAL (operands[2]) <= 65535)))"
[(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
(parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])
(set (reg:QI B_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
(set (reg:QI A_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
(parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])]
"operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
 
;;
;; Instruction generated to propagate the carry of a 16-bit add
;; to the upper 16-bit part (in register X).
;;
(define_insn "*addsi_carry"
[(set (match_operand:HI 0 "register_operand" "=x")
(plus:HI (plus:HI (match_operand:HI 1 "register_operand" "0")
(const_int 0))
(reg:HI CC_REGNUM)))]
""
"*
{
rtx ops[2];
 
ops[0] = gen_label_rtx ();
output_asm_insn (\"bcc\\t%l0\", ops);
output_asm_insn (\"in%0\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
CC_STATUS_INIT;
return \"\";
}")
 
;;
;; - 16-bit Add.
;;
(define_expand "addhi3"
[(set (match_operand:HI 0 "register_operand" "")
(plus:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (TARGET_M6811 && SP_REG_P (operands[0]))
{
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_PLUS (HImode,
operand1, operand2)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (HImode)))));
DONE;
}
}")
 
(define_insn "*addhi3_68hc12"
[(set (match_operand:HI 0 "register_operand" "=d*A,d,xy*A*w,xy*A*w,xy*A")
(plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,xy*Aw,0")
(match_operand:HI 2 "general_operand" "i,m*A*wu,id,id,!mu*A")))]
"TARGET_M6812"
"*
{
int val;
const char* insn_code;
 
if (which_alternative >= 4)
{
if (A_REG_P (operands[2]))
{
CC_STATUS_INIT;
output_asm_insn (\"xgd%2\", operands);
output_asm_insn (\"lea%0 d,%0\", operands);
return \"xgd%2\";
}
return \"#\";
}
 
if (D_REG_P (operands[0]))
{
if (X_REG_P (operands[2]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn (\"xgdx\", operands);
output_asm_insn (\"leax\\td,%2\", operands);
return \"xgdx\";
}
else if (Y_REG_P (operands[2]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn (\"xgdy\", operands);
output_asm_insn (\"leay\\td,%2\", operands);
return \"xgdy\";
}
else if (SP_REG_P (operands[2]))
{
output_asm_insn (\"sts\\t%t0\", operands);
return \"addd\\t%t0\";
}
return \"addd\\t%2\";
}
 
if (GET_CODE (operands[2]) == CONST_INT)
val = INTVAL (operands[2]);
else
val = 1000;
 
if ((val != -1 && val != 1) || !rtx_equal_p (operands[0], operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
switch (REGNO (operands[0]))
{
case HARD_X_REGNUM:
return \"leax\\t%i2,%1\";
 
case HARD_Y_REGNUM:
return \"leay\\t%i2,%1\";
 
case HARD_SP_REGNUM:
return \"leas\\t%i2,%1\";
 
default:
fatal_insn (\"Invalid operands in the instruction\", insn);
}
}
if (val > 0)
{
insn_code = X_REG_P (operands[0]) ? \"inx\"
: Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
}
else
{
val = -val;
insn_code = X_REG_P (operands[0]) ? \"dex\"
: Y_REG_P (operands[0]) ? \"dey\" : \"des\";
}
 
/* For X and Y increment, the flags are not complete. Only the Z flag
is updated. For SP increment, flags are not changed. */
if (SP_REG_P (operands[0]))
{
cc_status = cc_prev_status;
if (INTVAL (operands[2]) < 0)
{
while (val > 2)
{
output_asm_insn (\"pshx\", operands);
val -= 2;
}
if (val == 0)
return \"\";
}
}
else
{
CC_STATUS_INIT;
}
 
while (val)
{
output_asm_insn (insn_code, operands);
val--;
}
return \"\";
}")
 
;;
;; Specific pattern to add to the stack pointer.
;; We also take care of the clobbering of the IY register.
;;
(define_insn "addhi_sp"
[(set (match_operand:HI 0 "stack_register_operand" "=w,w,w,w")
(plus:HI (match_operand:HI 1 "stack_register_operand" "%0,0,0,0")
(match_operand:HI 2 "general_operand" "P,im,u,im")))
(clobber (match_scratch:HI 3 "=X,&y,&y,!&x"))]
"!TARGET_M6812"
"*
{
HOST_WIDE_INT val;
 
if (optimize && Y_REG_P (operands[3])
&& dead_register_here (insn, gen_rtx_REG (HImode, HARD_X_REGNUM)))
operands[3] = gen_rtx_REG (HImode, HARD_X_REGNUM);
 
if (GET_CODE (operands[2]) == CONST_INT
&& (val = INTVAL (operands[2])) != 0
&& (CONST_OK_FOR_LETTER_P (val, 'P')
|| (val > 0 && val <= 8)))
{
while (val > 1 || val < -1)
{
if (val > 0)
{
if (!H_REG_P (operands[3]))
break;
 
output_asm_insn (\"pul%3\", operands);
val -= 2;
}
else
{
output_asm_insn (\"pshx\", operands);
val += 2;
}
}
while (val != 0)
{
if (val > 0)
{
output_asm_insn (\"ins\", operands);
val--;
}
else
{
output_asm_insn (\"des\", operands);
val++;
}
}
cc_status = cc_prev_status;
return \"\";
}
 
/* Need to transfer to SP to X/Y and then to D register.
Register X/Y is lost, this is specified by the (clobber) statement. */
output_asm_insn (\"ts%3\", operands);
if (GET_CODE (operands[2]) == CONST_INT
&& ((val = INTVAL (operands[2])) >= 0 && val < 0x100)
&& dead_register_here (insn, gen_rtx_REG (HImode, HARD_D_REGNUM)))
{
output_asm_insn (\"ldab\\t%2\", operands);
output_asm_insn (\"ab%3\", operands);
CC_STATUS_INIT;
}
else
{
output_asm_insn (\"xgd%3\", operands);
output_asm_insn (\"addd\\t%2\", operands);
output_asm_insn (\"xgd%3\", operands);
}
 
/* The status flags correspond to the addd. xgdy and tys do not
modify the flags. */
return \"t%3s\";
}")
 
(define_insn "*addhi3"
[(set (match_operand:HI 0 "hard_reg_operand" "=A,dA,d,!A,d*A,d,!d*A")
(plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0,0,0,0")
(match_operand:HI 2 "general_operand" "N,I,i,I,mi*A*d,*u,!u*d*w")))]
"TARGET_M6811"
"*
{
const char* insn_code;
int val;
 
if (D_REG_P (operands[0]) && SP_REG_P (operands[2]))
{
output_asm_insn (\"sts\\t%t0\", operands);
output_asm_insn (\"addd\\t%t0\", operands);
return \"addd\\t#1\";
}
if (GET_CODE (operands[2]) != CONST_INT)
{
/* Adding to an address register or with another/same register
is not possible. This must be replaced. */
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
return \"addd\\t%2\";
}
val = INTVAL (operands[2]);
if (!SP_REG_P (operands[0]))
{
if (D_REG_P (operands[0]))
{
if ((val & 0x0ff) == 0 && !next_insn_test_reg (insn, operands[0]))
{
CC_STATUS_INIT;
return \"adda\\t%h2\";
}
else
{
return \"addd\\t%2\";
}
}
else if (GET_CODE (operands[2]) != CONST_INT
|| INTVAL (operands[2]) < -4
|| INTVAL (operands[2]) > 4)
return \"#\";
}
if (val > 0)
{
insn_code = X_REG_P (operands[0]) ? \"inx\"
: Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
}
else
{
val = -val;
insn_code = X_REG_P (operands[0]) ? \"dex\"
: Y_REG_P (operands[0]) ? \"dey\" : \"des\";
}
 
/* For X and Y increment, the flags are not complete. Only the Z flag
is updated. For SP increment, flags are not changed. */
if (SP_REG_P (operands[0]))
{
cc_status = cc_prev_status;
if (INTVAL (operands[2]) < 0)
{
while (val >= 2)
{
output_asm_insn (\"pshx\", operands);
val -= 2;
}
}
else if (optimize && dead_register_here (insn, ix_reg))
{
while (val >= 2)
{
output_asm_insn (\"pulx\", operands);
val -= 2;
}
}
}
else
{
CC_STATUS_INIT;
}
 
while (val)
{
output_asm_insn (insn_code, operands);
val--;
}
return \"\";
}")
 
(define_insn "*addhi3_zext"
[(set (match_operand:HI 0 "hard_reg_operand" "=A,d")
(plus:HI (zero_extend:HI
(match_operand:QI 1 "nonimmediate_operand" "d,um*A"))
(match_operand:HI 2 "general_operand" "0,0")))]
""
"*
{
CC_STATUS_INIT;
if (A_REG_P (operands[0]))
return \"ab%0\";
else if (A_REG_P (operands[1]))
return \"st%1\\t%t0\\n\\taddb\\t%T0\\n\\tadca\\t#0\";
else
return \"addb\\t%b1\\n\\tadca\\t#0\";
}")
 
;;
;; Translate d = d + d into d = << 1
;; We have to do this because adding a register to itself is not possible.
;; ??? It's not clear whether this is really necessary.
;;
(define_split
[(set (match_operand:QI 0 "hard_reg_operand" "")
(plus:QI (match_dup 0)
(match_dup 0)))]
"0 && reload_completed"
[(set (match_dup 0) (ashift:QI (match_dup 0) (const_int 1)))]
"")
 
(define_insn "addqi3"
[(set (match_operand:QI 0 "nonimmediate_operand" "=!d*rm,dq,!*A")
(plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
(match_operand:QI 2 "general_operand" "N,ium*A*d,ium*A*d")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT)
{
if (INTVAL (operands[2]) == 1)
{
if (DA_REG_P (operands[0]))
{
return \"inca\";
}
else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
{
return \"incb\";
 
}
else if (A_REG_P (operands[0]))
{
/* This applies on the 16-bit register. This should be ok since
this is not a strict_low_part increment. */
return \"in%0\";
}
else
{
return \"inc\\t%b0\";
}
}
else if (INTVAL (operands[2]) == -1)
{
if (DA_REG_P (operands[0]))
{
return \"deca\";
}
else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
{
return \"decb\";
}
else if (A_REG_P (operands[0]))
{
/* This applies on the 16-bit register. This should be ok since
this is not a strict_low_part decrement. */
return \"de%0\";
}
else
{
return \"dec\\t%b0\";
}
}
}
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"addb\\t%b2\";
else
return \"adda\\t%b2\";
}")
 
;;
;; add with carry is used for 32-bit add.
;;
(define_insn "*adcq"
[(set (match_operand:QI 0 "register_operand" "=q")
(plus:QI (plus:QI (reg:QI CC_REGNUM)
(match_operand:QI 1 "register_operand" "%0"))
(match_operand:QI 2 "general_operand" "ium")))]
""
"adc%0\\t%b2")
 
;;--------------------------------------------------------------------
;;- Subtract instructions.
;;--------------------------------------------------------------------
 
(define_expand "subdi3"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(minus:DI (match_operand:DI 1 "nonimmediate_operand" "")
(match_operand:DI 2 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"___subdi3\", MINUS, DImode, DImode, 3, operands);
DONE;")
 
;;
;; 32-bit Subtract (see addsi3)
;; Subtract with a constant are handled by addsi3.
;;
;;
;; - 32-bit Add.
;;
(define_expand "subsi3"
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 ""))])]
""
"")
 
(define_insn "*subsi3"
[(set (match_operand:SI 0 "register_operand" "=D,D,D,D,!u")
(minus:SI (match_operand:SI 1 "general_operand" "0,oi,0,!u,0")
(match_operand:SI 2 "general_operand" "oi,D,!u,D,!oui")))
(clobber (match_scratch:HI 3 "=X,X,X,X,d"))]
""
"#")
 
(define_insn "*subsi3_zero_extendhi"
[(set (match_operand:SI 0 "register_operand" "=D")
(minus:SI (match_operand:SI 1 "register_operand" "0")
(zero_extend:SI (match_operand:HI 2 "general_operand" "dmui*A"))))
(clobber (match_scratch:HI 3 "=X"))]
""
"*
{
rtx ops[2];
 
if (A_REG_P (operands[2]))
{
if (TARGET_M6812)
ops[0] = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
 
ops[1] = operands[2];
m68hc11_gen_movhi (insn, ops);
if (TARGET_M6812)
operands[2] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
operands[2] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
}
ops[0] = gen_label_rtx ();
output_asm_insn (\"subd\\t%2\", operands);
output_asm_insn (\"bcc\\t%l0\", ops);
output_asm_insn (\"dex\", ops);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
CC_STATUS_INIT;
return \"\";
}")
 
(define_insn "*subsi3_zero_extendqi"
[(set (match_operand:SI 0 "register_operand" "=D")
(minus:SI (match_operand:SI 1 "register_operand" "0")
(zero_extend:SI (match_operand:QI 2 "general_operand" "dmui*A"))))
(clobber (match_scratch:HI 3 "=X"))]
""
"*
{
rtx ops[2];
 
if (A_REG_P (operands[2]))
{
ops[0] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movhi (insn, ops);
operands[2] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
}
ops[0] = gen_label_rtx ();
output_asm_insn (\"subb\\t%b2\", operands);
output_asm_insn (\"sbca\\t#0\", operands);
output_asm_insn (\"bcc\\t%l0\", ops);
output_asm_insn (\"dex\", ops);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
CC_STATUS_INIT;
return \"\";
}")
 
;;
;; reg:HI 1 -> d reg:QI 6 -> B
;; reg:QI 7 -> ccr reg:QI 5 -> A
;;
(define_split /* "*subsi3" */
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 "=X"))]
"reload_completed && z_replacement_completed == 2
&& X_REG_P (operands[1])"
[(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
(parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
(set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
(set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
(parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])]
"operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
 
(define_split /* "*subsi3" */
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "register_operand" "")))
(clobber (match_scratch:HI 3 "=X"))]
"reload_completed && z_replacement_completed == 2
&& X_REG_P (operands[2])"
[(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
(parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
(set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
(set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
(parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
(set (reg:SI 0) (neg:SI (reg:SI 0)))]
"operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
operands[4] = m68hc11_gen_highpart (HImode, operands[1]);
operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")
 
(define_split /* "*subsi3" */
[(set (match_operand:SI 0 "nonimmediate_operand" "")
(minus:SI (match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 "=d"))]
"reload_completed && z_replacement_completed == 2
&& !X_REG_P (operands[0])"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 3) (minus:HI (match_dup 3) (match_dup 5)))
(set (match_dup 4) (match_dup 3))
(set (match_dup 3) (match_dup 6))
(set (reg:QI 6) (minus:QI (minus:QI (reg:QI 7) (reg:QI 6)) (match_dup 7)))
(set (reg:QI 5) (minus:QI (minus:QI (reg:QI 7) (reg:QI 5)) (match_dup 8)))
(set (match_dup 6) (match_dup 3))]
"operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
operands[7] = m68hc11_gen_highpart (HImode, operands[2]);
operands[8] = m68hc11_gen_highpart (QImode, operands[7]);
operands[7] = m68hc11_gen_lowpart (QImode, operands[7]);")
 
;;
;; - 16-bit Subtract.
;;
(define_expand "subhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(minus:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
""
"")
 
;;
;; Subtract from stack. This is better if we provide a pattern.
;;
(define_insn "*subhi3_sp"
[(set (match_operand:HI 0 "stack_register_operand" "=w,w")
(minus:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "im*d,!u*A")))
(clobber (match_scratch:HI 3 "=A*d,A*d"))]
""
"*
{
if (X_REG_P (operands[2]))
{
operands[2] = m68hc11_soft_tmp_reg;
output_asm_insn (\"stx\\t%2\", operands);
}
else if (Y_REG_P (operands[2]))
{
operands[2] = m68hc11_soft_tmp_reg;
output_asm_insn (\"sty\\t%2\", operands);
}
else if (D_REG_P (operands[2]))
{
operands[2] = m68hc11_soft_tmp_reg;
output_asm_insn (\"std\\t%2\", operands);
}
 
if (D_REG_P (operands[3]))
{
int save_x;
 
save_x = !dead_register_here (insn, ix_reg);
if (save_x)
output_asm_insn (\"xgdx\", operands);
output_asm_insn (\"tsx\", operands);
output_asm_insn (\"xgdx\", operands);
output_asm_insn (\"subd\\t%2\", operands);
output_asm_insn (\"xgdx\", operands);
 
/* The status flags correspond to the addd. xgdx/y and tx/ys do not
modify the flags. */
output_asm_insn (\"txs\", operands);
if (save_x)
return \"xgdx\";
else
return \"\";
}
 
/* Need to transfer to SP to X,Y and then to D register.
Register X,Y is lost, this is specified by the (clobber) statement. */
output_asm_insn (\"ts%3\", operands);
output_asm_insn (\"xgd%3\", operands);
output_asm_insn (\"subd\\t%2\", operands);
output_asm_insn (\"xgd%3\", operands);
 
/* The status flags correspond to the addd. xgdx/y and tx/ys do not
modify the flags. */
return \"t%3s\";
}")
 
 
(define_insn "*subhi3"
[(set (match_operand:HI 0 "register_operand" "=d,*A,d,*A")
(minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
(match_operand:HI 2 "general_operand" "im*A*d,im*d*A,u,!u")))]
""
"*
{
/* Adding to an address register or with another/same register
is not possible. This must be replaced. */
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
return \"subd\\t%2\";
}")
 
(define_insn "*subhi3_zext"
[(set (match_operand:HI 0 "hard_reg_operand" "=d,d")
(minus:HI (match_operand:HI 1 "general_operand" "0,0")
(zero_extend:HI (match_operand:QI 2 "general_operand" "mi*A,!u"))))]
""
"*
{
CC_STATUS_INIT;
if (A_REG_P (operands[2]))
{
rtx ops[2];
 
ops[0] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
return \"subb\\t%T0\\n\\tsbca\\t#0\";
}
return \"subb\\t%b2\\n\\tsbca\\t#0\";
}")
 
(define_insn "subqi3"
[(set (match_operand:QI 0 "hard_reg_operand" "=dq,!*x*y")
(minus:QI (match_operand:QI 1 "general_operand" "0,0")
(match_operand:QI 2 "general_operand" "uim*A*d,uim*A*d")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"subb\\t%b2\";
else
return \"suba\\t%b2\";
}")
 
;;
;; subtract with carry is used for 32-bit subtract.
;;
(define_insn "*subcq"
[(set (match_operand:QI 0 "register_operand" "=q")
(minus:QI (minus:QI (reg:QI CC_REGNUM)
(match_operand:QI 1 "register_operand" "0"))
(match_operand:QI 2 "general_operand" "ium")))]
""
"sbc%0\\t%b2")
 
;;--------------------------------------------------------------------
;;- Multiply instructions.
;;--------------------------------------------------------------------
;;
;; 32 and 64-bit multiply are handled by the library
;;
 
(define_expand "mulsi3"
[(set (match_operand:SI 0 "nonimmediate_operand" "")
(mult:SI (match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"__mulsi3\", MULT, SImode, SImode, 3, operands);
DONE;")
 
(define_expand "mulhi3"
[(parallel [(set (match_operand:HI 0 "register_operand" "")
(mult:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "register_operand" "")))
(clobber (match_scratch:HI 3 ""))])]
""
"")
 
(define_insn "mulhi3_m68hc11"
[(set (match_operand:HI 0 "register_operand" "=d")
(mult:HI (match_operand:HI 1 "register_operand" "%0")
(match_operand:HI 2 "register_operand" "x")))
(clobber (match_scratch:HI 3 "=X"))]
"TARGET_M6811"
"*
{
CC_STATUS_INIT;
/* D * X -> D (X and Y are preserved by this function call). */
return \"jsr\\t___mulhi3\";
}")
 
(define_insn "mulhi3_m68hc12"
[(set (match_operand:HI 0 "register_operand" "=d,d")
(mult:HI (match_operand:HI 1 "register_operand" "%0,0")
(match_operand:HI 2 "register_operand" "y,x")))
(clobber (match_scratch:HI 3 "=2,2"))]
"TARGET_M6812"
"*
{
CC_STATUS_INIT;
if (X_REG_P (operands[2]))
return \"exg\\tx,y\\n\\temul\\n\\texg\\tx,y\";
else
return \"emul\";
}")
 
(define_insn "umulhisi3"
[(set (match_operand:SI 0 "register_operand" "=D,D")
(mult:SI (zero_extend:SI
(match_operand:HI 1 "register_operand" "%d,d"))
(zero_extend:SI
(match_operand:HI 2 "register_operand" "y,x"))))
(clobber (match_scratch:HI 3 "=2,X"))]
"TARGET_M6812"
"*
{
if (X_REG_P (operands [2]))
output_asm_insn (\"exg\\tx,y\", operands);
 
/* Can't use the carry after that; other flags are ok when testing
the 32-bit result. */
cc_status.flags |= CC_NO_OVERFLOW;
return \"emul\\n\\texg\\tx,y\";
}")
 
(define_insn "mulhisi3"
[(set (match_operand:SI 0 "register_operand" "=D,D")
(mult:SI (sign_extend:SI
(match_operand:HI 1 "register_operand" "%d,d"))
(sign_extend:SI
(match_operand:HI 2 "register_operand" "y,x"))))
(clobber (match_scratch:HI 3 "=2,X"))]
"TARGET_M6812"
"*
{
if (X_REG_P (operands [2]))
output_asm_insn (\"exg\\tx,y\", operands);
 
/* Can't use the carry after that; other flags are ok when testing
the 32-bit result. */
cc_status.flags |= CC_NO_OVERFLOW;
return \"emuls\\n\\texg\\tx,y\";
}")
 
(define_insn "umulqihi3"
[(set (match_operand:HI 0 "register_operand" "=d")
(mult:HI (zero_extend:HI
(match_operand:QI 1 "nonimmediate_operand" "dm*u"))
(zero_extend:HI
(match_operand:QI 2 "nonimmediate_operand" "dm*u*A"))))]
""
"*
{
if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
{
output_asm_insn (\"tba\", operands);
}
else
{
rtx ops[2];
 
if (D_REG_P (operands[2]))
{
rtx temp = operands[2];
operands[2] = operands[1];
operands[1] = temp;
}
 
ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
 
if (!D_REG_P (operands[1]))
{
output_asm_insn (\"ldab\\t%b1\", operands);
}
}
 
CC_STATUS_INIT;
return \"mul\";
}")
 
(define_insn "mulqi3"
[(set (match_operand:QI 0 "register_operand" "=d,*x,*y")
(mult:QI (match_operand:QI 1 "general_operand" "%di*um,0,0")
(match_operand:QI 2 "general_operand" "di*um,*xium,*yium")))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
{
output_asm_insn (\"tba\", operands);
}
else
{
if (D_REG_P (operands[2]))
{
rtx temp = operands[2];
operands[2] = operands[1];
operands[1] = temp;
}
output_asm_insn (\"ldaa\\t%b2\", operands);
 
if (!D_REG_P (operands[1]))
{
output_asm_insn (\"ldab\\t%b1\", operands);
}
}
 
CC_STATUS_INIT;
return \"mul\";
}")
 
(define_split
[(set (match_operand:QI 0 "hard_addr_reg_operand" "")
(mult:QI (match_operand:QI 1 "general_operand" "")
(match_operand:QI 2 "general_operand" "")))]
"z_replacement_completed == 2"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (mult:QI (match_dup 5) (match_dup 6)))
(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])]
"
operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
if (A_REG_P (operands[1]))
operands[5] = gen_rtx_REG (QImode, HARD_D_REGNUM);
else
operands[5] = operands[1];
if (A_REG_P (operands[2]))
operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
else
operands[6] = operands[2];
")
 
(define_insn "mulqihi3"
[(set (match_operand:HI 0 "register_operand" "=d,d,d")
(mult:HI (sign_extend:HI
(match_operand:QI 1 "register_operand" "%0,0,0"))
(sign_extend:HI
(match_operand:QI 2 "general_operand" "mi*u,*A,0"))))]
""
"*
{
CC_STATUS_INIT;
 
/* Special case when multiplying the register with itself. */
if (D_REG_P (operands[2]))
{
output_asm_insn (\"tba\", operands);
return \"mul\";
}
 
if (!H_REG_P (operands[2]))
{
output_asm_insn (\"ldaa\\t%b2\", operands);
}
else
{
rtx ops[2];
 
ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
}
return \"jsr\\t___mulqi3\";
}")
 
;;--------------------------------------------------------------------
;;- Divide instructions.
;;--------------------------------------------------------------------
 
(define_insn "divmodhi4"
[(set (match_operand:HI 0 "register_operand" "=d,d")
(div:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "A,ium")))
(set (match_operand:HI 3 "register_operand" "=&x,&x")
(mod:HI (match_dup 1) (match_dup 2)))]
""
"*
{
if (!X_REG_P (operands[2]))
{
if (Y_REG_P (operands[2]))
{
output_asm_insn (\"sty\\t%t1\", operands);
output_asm_insn (\"ldx\\t%t1\", operands);
}
else
{
output_asm_insn (\"ldx\\t%2\", operands);
}
}
if (TARGET_M6812)
{
/* Flags are ok after that. */
return \"idivs\\n\\txgdx\";
}
else
{
CC_STATUS_INIT;
return \"bsr\\t__divmodhi4\";
}
}")
 
(define_insn "udivmodhi4"
[(set (match_operand:HI 0 "register_operand" "=d,d")
(udiv:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "A,ium")))
(set (match_operand:HI 3 "register_operand" "=x,x")
(umod:HI (match_dup 1) (match_dup 2)))]
""
"*
{
if (!X_REG_P (operands[2]))
{
if (Y_REG_P (operands[2]))
{
output_asm_insn (\"sty\\t%t1\", operands);
output_asm_insn (\"ldx\\t%t1\", operands);
}
else
{
output_asm_insn (\"ldx\\t%2\", operands);
}
}
 
/* Z V and C flags are set but N is unchanged.
Since this is an unsigned divide, we can probably keep the flags
and indicate this. */
cc_status.flags |= CC_NOT_NEGATIVE;
return \"idiv\\n\\txgdx\";
}")
 
;;--------------------------------------------------------------------
;;- and instructions.
;;--------------------------------------------------------------------
 
(define_insn_and_split "anddi3"
[(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
(and:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
(match_operand:DI 2 "general_operand" "imu,imu")))
(clobber (match_scratch:HI 3 "=d,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (SImode, AND, operands);
DONE;")
 
(define_insn_and_split "andsi3"
[(set (match_operand:SI 0 "register_operand" "=D,!u")
(and:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "general_operand" "Dimu,imu")))
(clobber (match_scratch:HI 3 "=X,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (HImode, AND, operands);
DONE;")
 
(define_expand "andhi3"
[(set (match_operand:HI 0 "register_operand" "")
(and:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"")
 
(define_insn "*andhi3_mem"
[(set (match_operand:HI 0 "memory_operand" "=R,Q")
(and:HI (match_dup 0)
(match_operand:HI 1 "immediate_operand" "i,i")))
(clobber (match_scratch:HI 2 "=X,xy"))]
"TARGET_RELAX && !TARGET_M6812"
"*
{
int val = INTVAL (operands[1]) & 0x0FFFF;
 
if (val == 0x0ffff)
{
cc_status = cc_prev_status;
return \"\";
}
 
CC_STATUS_INIT;
 
/* The bclr instruction uses an inverted mask. */
operands[1] = GEN_INT ((~val) & 0x0FFFF);
 
/* When destination is a global variable, generate a .relax instruction
and load the address in the clobber register. That load can be
eliminated by the linker if the address is in page0. */
if (which_alternative == 1)
{
rtx ops[3];
 
ops[0] = operands[2];
ops[1] = XEXP (operands[0], 0);
ops[2] = gen_label_rtx ();
output_asm_insn (\".relax\\t%l2\", ops);
m68hc11_gen_movhi (insn, ops);
if ((val & 0x0FF) != 0x0FF)
output_asm_insn (\"bclr\\t1,%2, %b1\", operands);
 
if ((val & 0x0FF00) != 0x0FF00)
output_asm_insn (\"bclr\\t0,%2, %h1\", operands);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[2]));
return \"\";
}
 
if ((val & 0x0FF) != 0x0FF)
output_asm_insn (\"bclr\\t%b0, %b1\", operands);
 
if ((val & 0x0FF00) != 0x0FF00)
output_asm_insn (\"bclr\\t%h0, %h1\", operands);
 
return \"\";
}")
 
(define_insn "*andhi3_const"
[(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
(and:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
(match_operand:HI 2 "const_int_operand" "")))]
""
"*
{
int val = INTVAL (operands[2]) & 0x0FFFF;
int lowpart_zero = 0;
int highpart_zero = 0;
int lowpart_unknown = 0;
int highpart_unknown = 0;
 
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (val == 0x0ffff)
{
cc_status = cc_prev_status;
return \"\";
}
 
/* First, try to clear the low and high part.
If that's possible, the second 'and' will give
the good status flags and we can avoid a tsthi. */
if ((val & 0x0FF) == 0)
{
if (D_REG_P (operands[0]))
output_asm_insn (\"clrb\", operands);
else
output_asm_insn (\"clr\\t%b0\", operands);
lowpart_zero = 1;
}
if ((val & 0x0FF00) == 0)
{
if (D_REG_P (operands[0]))
output_asm_insn (\"clra\", operands);
else
output_asm_insn (\"clr\\t%h0\", operands);
highpart_zero = 1;
}
 
if ((val & 0x0FF) == 0x0FF)
{
lowpart_unknown = 1;
}
else if ((val & 0x0FF) != 0 && !H_REG_P (operands[0]))
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = GEN_INT ((~val) & 0x0FF);
output_asm_insn (\"bclr\\t%b0, %1\", ops);
}
else if ((val & 0x0FF) != 0)
{
output_asm_insn (\"andb\\t%b2\", operands);
}
 
if ((val & 0x0FF00) == 0x0FF00)
{
highpart_unknown = 1;
}
else if (((val & 0x0FF00) != 0) && !H_REG_P (operands[0]))
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = GEN_INT (((~val) & 0x0FF00) >> 8);
output_asm_insn (\"bclr\\t%h0, %1\", ops);
}
else if ((val & 0x0FF00) != 0)
{
output_asm_insn (\"anda\\t%h2\", operands);
}
 
if (highpart_unknown || lowpart_unknown)
CC_STATUS_INIT;
else if (highpart_zero == 0 && lowpart_zero == 0)
CC_STATUS_INIT;
 
return \"\";
}")
 
(define_insn "*andhi3_gen"
[(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
(and:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
(match_operand:HI 2 "splitable_operand" "mi,!u*A,!um*Ai")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
CC_STATUS_INIT;
return \"anda\\t%h2\\n\\tandb\\t%b2\";
}")
 
(define_expand "andqi3"
[(set (match_operand:QI 0 "register_operand" "")
(and:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))]
""
"")
 
(define_insn "*andqi3_mem"
[(set (match_operand:QI 0 "memory_operand" "=R,Q")
(and:QI (match_dup 0)
(match_operand:QI 1 "const_int_operand" "i,i")))
(clobber (match_scratch:HI 2 "=X,xy"))]
"TARGET_RELAX && !TARGET_M6812"
"*
{
int val = INTVAL (operands[1]) & 0x0FF;
 
if (val == 0x0ff)
{
cc_status = cc_prev_status;
return \"\";
}
 
/* The bclr instruction uses an inverted mask. */
operands[1] = GEN_INT ((~val) & 0x0FF);
 
/* When destination is a global variable, generate a .relax instruction
and load the address in the clobber register. That load can be
eliminated by the linker if the address is in page0. */
if (which_alternative == 1)
{
rtx ops[3];
 
ops[0] = operands[2];
ops[1] = XEXP (operands[0], 0);
ops[2] = gen_label_rtx ();
output_asm_insn (\".relax\\t%l2\", ops);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"bclr\\t0,%2, %1\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[2]));
return \"\";
}
return \"bclr\\t%b0, %1\";
}")
 
(define_insn "*andqi3_const"
[(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
(and:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
(match_operand:QI 2 "const_int_operand" "")))]
""
"*
{
int val = INTVAL (operands[2]) & 0x0FF;
 
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (val == 0x0ff)
{
cc_status = cc_prev_status;
return \"\";
}
if (!H_REG_P (operands[0]))
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = GEN_INT ((~val) & 0x0FF);
output_asm_insn (\"bclr\\t%b0, %b1\", ops);
return \"\";
}
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"andb\\t%b2\";
else if (DA_REG_P (operands[0]))
return \"anda\\t%b2\";
else
fatal_insn (\"Invalid operand in the instruction\", insn);
}")
 
(define_insn "*andqi3_gen"
[(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
(and:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
(match_operand:QI 2 "general_operand" "mi,!*u,?*A,!*um,?*A*d,!*um*A")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"andb\\t%b2\";
else if (DA_REG_P (operands[0]))
return \"anda\\t%b2\";
else
fatal_insn (\"Invalid operand in the instruction\", insn);
}")
 
;;--------------------------------------------------------------------
;;- Bit set or instructions.
;;--------------------------------------------------------------------
 
(define_insn_and_split "iordi3"
[(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
(ior:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
(match_operand:DI 2 "general_operand" "imu,imu")))
(clobber (match_scratch:HI 3 "=d,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (SImode, IOR, operands);
DONE;")
 
(define_insn_and_split "iorsi3"
[(set (match_operand:SI 0 "register_operand" "=D,!u")
(ior:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "general_operand" "Dimu,imu")))
(clobber (match_scratch:HI 3 "=X,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (HImode, IOR, operands);
DONE;")
 
(define_expand "iorhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ior:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "splitable_operand" "")))]
""
"")
 
(define_insn "*iorhi3_mem"
[(set (match_operand:HI 0 "memory_operand" "=R,Q")
(ior:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_scratch:HI 2 "=X,xy"))]
"TARGET_RELAX && !TARGET_M6812"
"*
{
int val = INTVAL (operands[1]) & 0x0FFFF;
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
CC_STATUS_INIT;
if (which_alternative == 1)
{
rtx ops[3];
 
ops[0] = operands[2];
ops[1] = XEXP (operands[0], 0);
ops[2] = gen_label_rtx ();
output_asm_insn (\".relax\\t%l2\", ops);
m68hc11_gen_movhi (insn, ops);
if ((val & 0x0FF) != 0)
output_asm_insn (\"bset\\t1,%2, %b1\", operands);
 
if ((val & 0x0FF00) != 0)
output_asm_insn (\"bset\\t0,%2, %h1\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[2]));
return \"\";
}
 
if ((val & 0x0FF) != 0)
output_asm_insn (\"bset\\t%b0, %b1\", operands);
 
if ((val & 0x0FF00) != 0)
output_asm_insn (\"bset\\t%h0, %h1\", operands);
 
return \"\";
}")
 
(define_insn "*iorhi3_const"
[(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
(ior:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
(match_operand:HI 2 "const_int_operand" "")))]
""
"*
{
int val = INTVAL (operands[2]) & 0x0FFFF;
 
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
 
if ((val & 0x0FF) != 0)
{
if (!H_REG_P (operands[0]))
output_asm_insn (\"bset\\t%b0, %b2\", operands);
else
output_asm_insn (\"orab\\t%b2\", operands);
}
 
if ((val & 0x0FF00) != 0)
{
if (!H_REG_P (operands[0]))
output_asm_insn (\"bset\\t%h0, %h2\", operands);
else
output_asm_insn (\"oraa\\t%h2\", operands);
}
 
CC_STATUS_INIT;
return \"\";
}")
 
(define_insn "*iorhi3_gen"
[(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
(ior:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
(match_operand:HI 2 "splitable_operand" "mi,!u*A,!um*Ai")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
CC_STATUS_INIT;
return \"oraa\\t%h2\\n\\torab\\t%b2\";
}")
 
(define_expand "iorqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ior:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))]
""
"")
 
(define_insn "*iorqi3_mem"
[(set (match_operand:QI 0 "memory_operand" "=R,Q")
(ior:QI (match_dup 0)
(match_operand:QI 1 "const_int_operand" "")))
(clobber (match_scratch:HI 2 "=X,xy"))]
"TARGET_RELAX && !TARGET_M6812"
"*
{
int val = INTVAL (operands[1]) & 0x0FF;
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
if (which_alternative == 1)
{
rtx ops[3];
 
ops[0] = operands[2];
ops[1] = XEXP (operands[0], 0);
ops[2] = gen_label_rtx ();
output_asm_insn (\".relax\\t%l2\", ops);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"bset\\t0,%2, %1\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[2]));
return \"\";
}
return \"bset\\t%b0, %1\";
}")
 
(define_insn "*iorqi3_const"
[(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
(ior:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
(match_operand:QI 2 "const_int_operand" "")))]
""
"*
{
int val = INTVAL (operands[2]) & 0x0FF;
 
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
if (!H_REG_P (operands[0]))
{
return \"bset\\t%b0, %2\";
}
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"orab\\t%b2\";
else if (DA_REG_P (operands[0]))
return \"oraa\\t%b2\";
else
fatal_insn (\"Invalid operand in the instruction\", insn);
}")
 
(define_insn "*iorqi3_gen"
[(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
(ior:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
(match_operand:QI 2 "general_operand" "mi,!*u,!*A,!*um,?*A*d,!*um*A")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"orab\\t%b2\";
else if (DA_REG_P (operands[0]))
return \"oraa\\t%b2\";
else
fatal_insn (\"Invalid operand in the instruction\", insn);
}")
 
 
;;--------------------------------------------------------------------
;;- xor instructions.
;;--------------------------------------------------------------------
 
(define_insn_and_split "xordi3"
[(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
(xor:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
(match_operand:DI 2 "general_operand" "imu,imu")))
(clobber (match_scratch:HI 3 "=d,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (SImode, XOR, operands);
DONE;")
 
(define_insn_and_split "xorsi3"
[(set (match_operand:SI 0 "register_operand" "=D,!u")
(xor:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "general_operand" "Dimu,imu")))
(clobber (match_scratch:HI 3 "=X,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_logical (HImode, XOR, operands);
DONE;")
 
(define_insn "xorhi3"
[(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
(xor:HI (match_operand:HI 1 "splitable_operand" "%0,0,0")
(match_operand:HI 2 "splitable_operand" "im,!u*A,!ium*A")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (GET_CODE (operands[2]) == CONST_INT)
{
int val = INTVAL (operands[2]) & 0x0FFFF;
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
if ((val & 0x0FF) != 0)
{
output_asm_insn (\"eorb\\t%b2\", operands);
}
else if ((val & 0x0FF) == 0x0FF)
{
output_asm_insn (\"comb\", operands);
}
 
if ((val & 0x0FF00) != 0)
{
output_asm_insn (\"eora\\t%h2\", operands);
}
else if ((val & 0x0FF00) == 0x0FF00)
{
output_asm_insn (\"coma\", operands);
}
 
CC_STATUS_INIT;
return \"\";
}
 
CC_STATUS_INIT;
return \"eora\\t%h2\\n\\teorb\\t%b2\";
}")
 
(define_insn "xorqi3"
[(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
(xor:QI (match_operand:QI 1 "general_operand" "%0,0,0,0,0,0")
(match_operand:QI 2 "general_operand" "im,!*u,!*A,!i*um,?*A*d,!i*um*A")))]
""
"*
{
if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
return \"#\";
 
if (GET_CODE (operands[2]) == CONST_INT)
{
int val = INTVAL (operands[2]) & 0x0FF;
 
if (val == 0)
{
cc_status = cc_prev_status;
return \"\";
}
if (val == 0x0FF)
{
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"comb\";
else
return \"coma\";
}
}
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
return \"eorb\\t%b2\";
else if (DA_REG_P (operands[0]))
return \"eora\\t%b2\";
else
fatal_insn (\"Invalid operand in the instruction\", insn);
}")
 
;;--------------------------------------------------------------------
;;- Bit set or instructions.
;;--------------------------------------------------------------------
 
(define_insn_and_split "*logicalsi3_zexthi"
[(set (match_operand:SI 0 "register_operand" "=D")
(match_operator:SI 3 "m68hc11_logical_operator"
[(zero_extend:SI
(match_operand:HI 1 "general_operand" "imudA"))
(match_operand:SI 2 "general_operand" "Dimu")]))]
""
"#"
"reload_completed"
[(set (reg:HI D_REGNUM) (match_dup 4))
(set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
(set (reg:HI X_REGNUM) (match_dup 6))]
"PUT_MODE (operands[3], HImode);
if (X_REG_P (operands[2]))
{
operands[5] = operands[1];
/* Make all the (set (REG:x) (REG:y)) a nop set. */
operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
}
else
{
operands[4] = operands[1];
operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
}
/* For an AND, make sure the high 16-bit part is cleared. */
if (GET_CODE (operands[3]) == AND)
{
operands[6] = const0_rtx;
}
")
 
(define_insn_and_split "*logicalsi3_zextqi"
[(set (match_operand:SI 0 "register_operand" "=D,D,D")
(match_operator:SI 3 "m68hc11_logical_operator"
[(zero_extend:SI
(match_operand:QI 1 "general_operand" "d,*A,imu"))
(match_operand:SI 2 "general_operand" "imu,imu,0")]))]
""
"#"
"z_replacement_completed == 2"
[(set (reg:QI A_REGNUM) (match_dup 4))
(set (reg:QI D_REGNUM) (match_dup 7))
(set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))
(set (reg:HI X_REGNUM) (match_dup 6))]
"PUT_MODE (operands[3], QImode);
if (X_REG_P (operands[2]))
{
operands[5] = operands[1];
/* Make all the (set (REG:x) (REG:y)) a nop set. */
operands[4] = gen_rtx_REG (QImode, HARD_A_REGNUM);
operands[7] = gen_rtx_REG (QImode, HARD_D_REGNUM);
operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
}
else
{
operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[7] = operands[1];
operands[5] = m68hc11_gen_lowpart (QImode, operands[4]);
operands[4] = m68hc11_gen_highpart (QImode, operands[4]);
operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
}
/* For an AND, make sure the high 24-bit part is cleared. */
if (GET_CODE (operands[3]) == AND)
{
operands[4] = const0_rtx;
operands[6] = const0_rtx;
}
")
 
(define_insn_and_split "*logicalhi3_zexthi_ashift8"
[(set (match_operand:HI 0 "register_operand" "=d")
(match_operator:HI 3 "m68hc11_logical_operator"
[(zero_extend:HI
(match_operand:QI 1 "general_operand" "imud*A"))
(ashift:HI
(match_operand:HI 2 "general_operand" "imud*A")
(const_int 8))]))]
""
"#"
"z_replacement_completed == 2"
[(set (reg:QI A_REGNUM) (match_dup 4))
(set (reg:QI B_REGNUM) (match_dup 5))]
"
if (GET_CODE (operands[3]) == AND)
{
emit_insn (gen_movhi (operands[0], const0_rtx));
DONE;
}
else
{
operands[5] = operands[1];
if (D_REG_P (operands[2]))
{
operands[4] = gen_rtx_REG (QImode, HARD_B_REGNUM);
}
else
{
operands[4] = m68hc11_gen_lowpart (QImode, operands[2]);
}
}
")
 
(define_insn_and_split "*logicalhi3_zexthi"
[(set (match_operand:HI 0 "register_operand" "=d,d")
(match_operator:HI 3 "m68hc11_logical_operator"
[(zero_extend:HI
(match_operand:QI 1 "general_operand" "imd*A,?u"))
(match_operand:HI 2 "general_operand" "dim,?dimu")]))]
""
"#"
"z_replacement_completed == 2"
[(set (reg:QI B_REGNUM) (match_dup 6))
(set (reg:QI A_REGNUM) (match_dup 4))
(set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))]
"
PUT_MODE (operands[3], QImode);
if (D_REG_P (operands[2]))
{
operands[4] = gen_rtx_REG (QImode, HARD_A_REGNUM);
operands[5] = operands[1];
operands[6] = gen_rtx_REG (QImode, HARD_B_REGNUM);
}
else
{
operands[4] = m68hc11_gen_highpart (QImode, operands[2]);
operands[5] = m68hc11_gen_lowpart (QImode, operands[2]);
if (D_REG_P (operands[1]))
operands[6] = gen_rtx_REG (QImode, HARD_B_REGNUM);
else
operands[6] = operands[1];
}
/* For an AND, make sure the high 8-bit part is cleared. */
if (GET_CODE (operands[3]) == AND)
{
operands[4] = const0_rtx;
}
")
 
 
(define_insn_and_split "*logicalsi3_silshr16"
[(set (match_operand:SI 0 "register_operand" "=D,D,D,?D")
(match_operator:SI 3 "m68hc11_logical_operator"
[(lshiftrt:SI
(match_operand:SI 1 "general_operand" "uim,uim,0,0")
(const_int 16))
(match_operand:SI 2 "general_operand" "uim,0,uim,0")]))]
""
"#"
"reload_completed"
[(set (reg:HI D_REGNUM) (match_dup 4))
(set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
(set (reg:HI X_REGNUM) (match_dup 6))]
"operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
if (X_REG_P (operands[2]))
{
operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
}
else
{
operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
}
PUT_MODE (operands[3], HImode);
 
/* For an AND, make sure the high 16-bit part is cleared. */
if (GET_CODE (operands[3]) == AND)
{
operands[6] = const0_rtx;
}
")
 
(define_insn_and_split "*logicalsi3_silshl16"
[(set (match_operand:SI 0 "register_operand" "=D,D")
(match_operator:SI 3 "m68hc11_logical_operator"
[(ashift:SI
(match_operand:SI 1 "general_operand" "uim,?D")
(const_int 16))
(match_operand:SI 2 "general_operand" "0,0")]))]
""
"#"
"z_replacement_completed == 2"
[(set (reg:HI X_REGNUM) (match_op_dup 3 [(reg:HI X_REGNUM) (match_dup 4)]))
(set (reg:HI D_REGNUM) (match_dup 5))]
"operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
PUT_MODE (operands[3], HImode);
 
if (GET_CODE (operands[3]) == AND)
operands[5] = const0_rtx;
else
operands[5] = gen_rtx_REG (HImode, HARD_D_REGNUM);
")
 
(define_insn_and_split "*logicalsi3_silshl16_zext"
[(set (match_operand:SI 0 "register_operand" "=D,D,D")
(match_operator:SI 3 "m68hc11_logical_operator"
[(ashift:SI
(zero_extend:SI
(match_operand:HI 1 "general_operand" "uim,udA,!dA"))
(const_int 16))
(zero_extend:SI (match_operand:HI 2 "general_operand" "uidA,um,!dA"))]))]
""
"#"
;; Must split before z register replacement
"reload_completed"
[(set (match_dup 4) (match_dup 5))
(set (match_dup 6) (match_dup 7))]
"
/* set (X_REGNUM) (d), set (D_REGNUM) (1) */
if (GET_CODE (operands[1]) == HARD_D_REGNUM
&& GET_CODE (operands[3]) != AND)
{
/* This particular case is too early to be split before
Z register replacement because the cse-reg pass we do
does not recognize the 'swap_areg'. It is ok to handle
this case after. */
if (z_replacement_completed != 2)
{
FAIL;
}
emit_move_insn (gen_rtx_REG (HImode, HARD_X_REGNUM), operands[2]);
emit_insn (gen_swap_areg (gen_rtx_REG (HImode, HARD_D_REGNUM),
gen_rtx_REG (HImode, HARD_X_REGNUM)));
}
operands[4] = gen_rtx_REG (HImode, HARD_D_REGNUM);
operands[6] = gen_rtx_REG (HImode, HARD_X_REGNUM);
operands[5] = operands[2];
operands[7] = operands[1];
 
if (GET_CODE (operands[3]) == AND)
operands[5] = operands[7] = const0_rtx;
")
 
;;--------------------------------------------------------------------
;; 16-bit Arithmetic and logical operations on X and Y:
;;
;; PLUS MINUS AND IOR XOR ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
;;
;; Operations on X or Y registers are split here. Instructions are
;; changed into:
;; - xgdx/xgdy instruction pattern,
;; - The same operation on register D,
;; - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
;;
(define_split
[(set (match_operand:HI 0 "hard_addr_reg_operand" "")
(match_operator:HI 3 "m68hc11_arith_operator"
[(match_operand:HI 1 "hard_addr_reg_operand" "")
(match_operand:HI 2 "general_operand" "")]))]
"z_replacement_completed == 2
/* If we are adding a small constant to X or Y, it's
better to use one or several inx/iny instructions. */
&& !(GET_CODE (operands[3]) == PLUS
&& ((TARGET_M6812
&& (immediate_operand (operands[2], HImode)
|| hard_reg_operand (operands[2], HImode)))
|| (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) >= -4
&& INTVAL (operands[2]) <= 4)))"
[(set (match_dup 9) (match_dup 0))
(set (match_dup 4) (match_dup 5))
(set (match_dup 8) (match_dup 7))
(set (match_dup 0) (match_dup 1))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 6)]))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"
operands[9] = operands[0];
/* For 68HC12, push the value on the stack and do the operation
with a pop. */
if (TARGET_M6812
&& m68hc11_non_shift_operator (operands[3], HImode)
&& (H_REG_P (operands[2])
|| (m68hc11_small_indexed_indirect_p (operands[2], HImode)
&& reg_mentioned_p (operands[0], operands[2]))))
{
operands[4] = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
operands[6] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
operands[5] = operands[2];
operands[8] = operands[7] = operands[0];
}
/* Save the operand2 in a temporary location and use it. */
else if ((H_REG_P (operands[2])
|| reg_mentioned_p (operands[0], operands[2]))
&& !(SP_REG_P (operands[2]) && GET_CODE (operands[3]) == PLUS))
{
if (GET_CODE (operands[3]) == MINUS
&& reg_mentioned_p (operands[0], operands[2]))
{
operands[9] = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
operands[1] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
operands[8] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[4] = operands[7] = operands[0];
operands[6] = operands[8];
operands[5] = operands[2];
}
else
{
operands[4] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[6] = operands[4];
if (!H_REG_P (operands[2]))
{
operands[5] = operands[0];
operands[7] = operands[2];
operands[8] = operands[0];
}
else
{
operands[5] = operands[2];
operands[8] = operands[7] = operands[0];
}
}
}
else
{
operands[4] = operands[5] = operands[0];
operands[6] = operands[2];
operands[8] = operands[7] = operands[0];
}
")
 
(define_split
[(set (match_operand:HI 0 "hard_addr_reg_operand" "")
(match_operator:HI 3 "m68hc11_arith_operator"
[(match_operand:HI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" "")]))]
"z_replacement_completed == 2
/* If we are adding a small constant to X or Y, it's
better to use one or several inx/iny instructions. */
&& !(GET_CODE (operands[3]) == PLUS
&& ((TARGET_M6812
&& (immediate_operand (operands[2], HImode)
|| hard_reg_operand (operands[2], HImode)))
|| (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) >= -4
&& INTVAL (operands[2]) <= 4)))"
[(set (match_dup 0) (match_dup 1))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 2)]))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"
")
 
;;
;; Next split handles the logical operations on D register with
;; another hard register for the second operand. For this, we
;; have to save the second operand in a scratch location and use
;; it instead. This must be supported because in some (rare) cases
;; the second operand can come in a hard register and the reload
;; pass doesn't know how to reload it in a memory location.
;;
;; PLUS MINUS AND IOR XOR
;;
;; The shift operators are special and must not appear here.
;;
(define_split
[(set (match_operand:HI 0 "d_register_operand" "")
(match_operator:HI 3 "m68hc11_non_shift_operator"
[(match_operand:HI 1 "d_register_operand" "")
(match_operand:HI 2 "hard_reg_operand" "")]))]
"TARGET_M6811
&& z_replacement_completed == 2 && !SP_REG_P (operands[2])"
[(set (match_dup 4) (match_dup 2))
(set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
"operands[4] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);")
 
;;
;; For 68HC12, push the operand[2] value on the stack and do the
;; logical/arithmetic operation with a pop.
;;
(define_split
[(set (match_operand:HI 0 "d_register_operand" "")
(match_operator:HI 3 "m68hc11_non_shift_operator"
[(match_operand:HI 1 "d_register_operand" "")
(match_operand:HI 2 "hard_reg_operand" "")]))]
"TARGET_M6812
&& z_replacement_completed == 2 && !SP_REG_P (operands[2])"
[(set (match_dup 4) (match_dup 2))
(set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 5)]))]
"operands[4] = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
operands[5] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
")
 
;;--------------------------------------------------------------------
;; 16-bit Unary operations on X and Y:
;;
;; NOT NEG
;;
;; Operations on X or Y registers are split here. Instructions are
;; changed into:
;; - xgdx/xgdy instruction pattern,
;; - The same operation on register D,
;; - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
(define_split
[(set (match_operand:HI 0 "hard_addr_reg_operand" "")
(match_operator:HI 2 "m68hc11_unary_operator"
[(match_operand 1 "general_operand" "")]))]
"z_replacement_completed == 2"
[(set (match_dup 4) (match_dup 5))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM) (match_op_dup 2 [(match_dup 3)]))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"
{
if ((H_REG_P (operands[1])
&& !rtx_equal_p (operands[0], operands[1]))
|| reg_mentioned_p (operands[0], operands[1]))
{
/* Move to the destination register, before the xgdx. */
operands[4] = gen_rtx_REG (GET_MODE (operands[1]),
REGNO (operands[0]));
operands[5] = operands[1];
 
/* Apply the operation on D. */
operands[3] = gen_rtx_REG (GET_MODE (operands[1]), HARD_D_REGNUM);
}
else
{
/* Generate a copy to same register (nop). */
operands[4] = operands[5] = operands[0];
operands[3] = operands[1];
}
}")
 
;;
;; 8-bit operations on address registers.
;;
;; We have to take care that the address register is not used for the
;; source of operand2. If operand2 is the D register, we have to save
;; that register in a temporary location.
;;
;; AND OR XOR PLUS MINUS ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
;;
(define_split
[(set (match_operand:QI 0 "hard_addr_reg_operand" "")
(match_operator:QI 3 "m68hc11_arith_operator"
[(match_operand:QI 1 "hard_addr_reg_operand" "")
(match_operand:QI 2 "general_operand" "")]))]
"z_replacement_completed == 2
/* Reject a (plus:QI (reg:QI X) (const_int 1|-1)) because the
incqi pattern generates a better code. */
&& !(GET_CODE (operands[3]) == PLUS
&& GET_CODE (operands[2]) == CONST_INT
&& (INTVAL (operands[2]) == 1 || INTVAL (operands[2]) == -1))"
[(set (match_dup 5) (match_dup 6))
(parallel [(set (reg:HI D_REGNUM) (match_dup 4))
(set (match_dup 4) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (match_op_dup 3 [(reg:QI D_REGNUM) (match_dup 7)]))
(parallel [(set (reg:HI D_REGNUM) (match_dup 4))
(set (match_dup 4) (reg:HI D_REGNUM))])]
"operands[4] = gen_rtx_REG (HImode, REGNO (operands[0]));
 
/* For the second operand is a hard register or if the address
register appears in the source, we have to save the operand[2]
value in a temporary location and then use that temp.
Otherwise, it's ok and we generate a (set (D) (D)) that
will result in a nop. */
if (H_REG_P (operands[2]))
{
operands[5] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[6] = gen_rtx_REG (HImode, REGNO (operands[2]));
operands[7] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
}
else if (reg_mentioned_p (operands[0], operands[2]))
{
operands[5] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
operands[6] = operands[2];
operands[7] = operands[5];
}
else
{
operands[5] = operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
operands[7] = operands[2];
}
")
 
;;
;; Next split handles the logical operations on D register with
;; another hard register for the second operand. For this, we
;; have to save the second operand in a scratch location and use
;; it instead. This must be supported because in some (rare) cases
;; the second operand can come in a hard register and the reload
;; pass doesn't know how to reload it in a memory location.
;;
;; PLUS MINUS AND IOR XOR
;;
;; The shift operators are special and must not appear here.
;;
(define_split
[(set (match_operand:QI 0 "d_register_operand" "")
(match_operator:QI 3 "m68hc11_non_shift_operator"
[(match_operand:QI 1 "d_register_operand" "")
(match_operand:QI 2 "hard_reg_operand" "")]))]
"reload_completed"
[(set (match_dup 5) (match_dup 6))
(set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
"operands[4] = gen_rtx_REG (QImode, SOFT_TMP_REGNUM);
operands[5] = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
operands[6] = gen_rtx_REG (HImode, REGNO (operands[2]));")
 
;;--------------------------------------------------------------------
;; 8-bit Unary operations on X and Y:
;;
;; NOT NEG
;;
;; Operations on X or Y registers are split here. Instructions are
;; changed into:
;; - xgdx/xgdy instruction pattern,
;; - The same operation on register D,
;; - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;; (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
(define_split
[(set (match_operand:QI 0 "hard_addr_reg_operand" "")
(match_operator:QI 2 "m68hc11_unary_operator"
[(match_operand:QI 1 "general_operand" "")]))]
"z_replacement_completed == 2"
[(set (match_dup 4) (match_dup 5))
(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (match_op_dup 2 [(match_dup 6)]))
(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
(set (match_dup 3) (reg:HI D_REGNUM))])]
"
{
operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
if ((H_REG_P (operands[1])
&& !rtx_equal_p (operands[0], operands[1]))
|| reg_mentioned_p (operands[0], operands[1]))
{
/* Move to the destination register, before the xgdx. */
operands[4] = operands[0];
operands[5] = operands[1];
 
/* Apply the operation on D. */
operands[6] = gen_rtx_REG (QImode, HARD_D_REGNUM);
}
else
{
operands[4] = operands[5] = operands[0];
operands[6] = operands[1];
}
}")
 
 
;;--------------------------------------------------------------------
;;- Complements
;;--------------------------------------------------------------------
 
(define_expand "negdi2"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(neg:DI (match_operand:DI 1 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"__negdi2\", NEG, DImode, DImode, 2, operands);
DONE;")
 
 
(define_insn "negsi2"
[(set (match_operand:SI 0 "register_operand" "=D")
(neg:SI (match_operand:SI 1 "general_operand" "0")))]
""
"*
{
rtx ops[1];
 
CC_STATUS_INIT;
 
/* With -Os or without -O, use a special library call. */
if (optimize_size || optimize == 0)
return \"bsr\\t___negsi2\";
 
ops[0] = gen_label_rtx ();
 
/* 32-bit complement and add 1. */
output_asm_insn (\"comb\\n\\tcoma\\n\\txgdx\", operands);
output_asm_insn (\"comb\\n\\tcoma\\n\\tinx\\n\\txgdx\", operands);
output_asm_insn (\"bne\\t%l0\", ops);
output_asm_insn (\"inx\", operands);
(*targetm.asm_out.internal_label) (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
return \"\";
}")
 
(define_insn "neghi2"
[(set (match_operand:HI 0 "register_operand" "=d,d,x*y")
(neg:HI (match_operand:HI 1 "general_operand" "0,!duim,0")))]
""
"@
coma\\n\\tcomb\\n\\taddd\\t#1
clra\\n\\tclrb\\n\\tsubd\\t%1
xgd%0\\n\\tcoma\\n\\tcomb\\n\\txgd%0\\n\\tin%0")
 
(define_insn "negqi2"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*A")
(neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")))]
""
"@
negb
neg\\t%b0
neg\\t%b0
#")
 
;;
;; - 32-bit complement. GCC knows how to translate them but providing a
;; pattern generates better/smaller code.
;;
(define_expand "one_cmpldi2"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(not:DI (match_operand:DI 1 "general_operand" "")))]
""
"m68hc11_emit_libcall (\"___notdi2\", NOT, DImode, DImode, 2, operands);
DONE;")
 
(define_insn "one_cmplsi2"
[(set (match_operand:SI 0 "non_push_operand" "=D,m,!u")
(not:SI (match_operand:SI 1 "general_operand" "0,m,0")))
(clobber (match_scratch:HI 2 "=X,d,X"))]
""
"@
bsr\\t___one_cmplsi2
#
#")
 
(define_insn "one_cmplhi2"
[(set (match_operand:HI 0 "non_push_operand" "=d,m,*A,u")
(not:HI (match_operand:HI 1 "general_operand" "0,0,0,0")))]
""
"@
comb\\n\\tcoma
com\\t%b0\\n\\tcom\\t%h0
#
com\\t%b0\\n\\tcom\\t%h0")
 
(define_insn "one_cmplqi2"
[(set (match_operand:QI 0 "non_push_operand" "=d,m,*A,u")
(not:QI (match_operand:QI 1 "general_operand" "0,0,0,0")))]
""
"@
comb
com\\t%b0
#
com\\t%b0")
 
(define_split /* "*one_cmplsi2" */
[(set (match_operand:SI 0 "non_push_operand" "")
(not:SI (match_dup 0)))
(clobber (match_scratch:HI 1 ""))]
"z_replacement_completed == 2
&& (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
[(set (match_dup 2) (not:HI (match_dup 2)))
(set (match_dup 3) (not:HI (match_dup 3)))]
"operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")
 
(define_split /* "*one_cmplsi2" */
[(set (match_operand:SI 0 "non_push_operand" "")
(not:SI (match_operand:SI 1 "non_push_operand" "")))
(clobber (match_operand:HI 2 "d_register_operand" ""))]
"z_replacement_completed == 2
&& (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 2) (not:HI (match_dup 2)))
(set (match_dup 4) (match_dup 2))
(set (match_dup 2) (match_dup 5))
(set (match_dup 2) (not:HI (match_dup 2)))
(set (match_dup 6) (match_dup 2))]
"operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
operands[6] = m68hc11_gen_highpart (HImode, operands[0]);")
 
;;--------------------------------------------------------------------
;;- arithmetic shifts
;;--------------------------------------------------------------------
;;
;; Provide some 64-bit shift patterns.
(define_expand "ashldi3"
[(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
(ashift:DI (match_operand:DI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 ""))])]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT
|| (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) != 1))
{
FAIL;
}
}")
 
(define_insn_and_split "*ashldi3_const32"
[(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
(ashift:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
(const_int 32)))
(clobber (match_scratch:HI 2 "=&A,d,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"/* Move the lowpart in the highpart first in case the shift
is applied on the source. */
if (IS_STACK_PUSH (operands[0]))
{
m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
const0_rtx, operands[2]);
 
/* Adjust first operand if it uses SP so that we take into
account the above push. Can occur only for 68HC12. */
if (reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM),
operands[1]))
operands[1] = adjust_address (operands[1],
GET_MODE (operands[0]), 4);
}
m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
m68hc11_gen_lowpart (SImode, operands[1]),
operands[2]);
if (!IS_STACK_PUSH (operands[0]))
{
m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
const0_rtx, operands[2]);
}
DONE;")
 
(define_insn_and_split "*ashldi3_const1"
[(set (match_operand:DI 0 "non_push_operand" "=m,m,u")
(ashift:DI (match_operand:DI 1 "general_operand" "mi,u,umi")
(const_int 1)))
(clobber (match_scratch:HI 2 "=d,d,d"))]
""
"#"
"z_replacement_completed == 2"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 2) (ashift:HI (match_dup 2) (const_int 1)))
(set (match_dup 4) (match_dup 2))
 
(set (match_dup 2) (match_dup 5))
(parallel [(set (match_dup 2)
(rotate:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 6) (match_dup 2))
 
(set (match_dup 2) (match_dup 7))
(parallel [(set (match_dup 2)
(rotate:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 8) (match_dup 2))
 
(set (match_dup 2) (match_dup 9))
(parallel [(set (match_dup 2)
(rotate:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 10) (match_dup 2))]
"operands[3] = m68hc11_gen_lowpart (SImode, operands[1]);
operands[5] = m68hc11_gen_highpart (HImode, operands[3]);
operands[3] = m68hc11_gen_lowpart (HImode, operands[3]);
 
operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
operands[6] = m68hc11_gen_highpart (HImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
 
operands[7] = m68hc11_gen_highpart (SImode, operands[1]);
operands[9] = m68hc11_gen_highpart (HImode, operands[7]);
operands[7] = m68hc11_gen_lowpart (HImode, operands[7]);
 
operands[8] = m68hc11_gen_highpart (SImode, operands[0]);
operands[10] = m68hc11_gen_highpart (HImode, operands[8]);
operands[8] = m68hc11_gen_lowpart (HImode, operands[8]);")
 
(define_insn "addsi_silshr16"
[(set (match_operand:SI 0 "register_operand" "=D,D,!D")
(plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "!*uim,0,0")
(const_int 16))
(match_operand:SI 2 "general_operand" "0,m!*u,0")))]
""
"#")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
(const_int 16))
(match_operand:SI 2 "general_operand" "")))]
"z_replacement_completed == 2 && !X_REG_P (operands[1])"
[(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
(set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
(const_int 0))
(reg:HI CC_REGNUM)))]
"operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
(const_int 16))
(match_operand:SI 2 "general_operand" "")))]
"z_replacement_completed == 2 && X_REG_P (operands[1])"
[(set (reg:HI D_REGNUM) (match_dup 5))
(set (reg:HI X_REGNUM) (match_dup 3))
(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 4)))
(set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
(const_int 0))
(reg:HI CC_REGNUM)))]
"operands[3] = m68hc11_gen_highpart (HImode, operands[2]);
if (X_REG_P (operands[2]))
{
operands[4] = gen_rtx_REG (HImode, HARD_X_REGNUM);
operands[5] = gen_rtx_REG (HImode, HARD_D_REGNUM);
}
else
{
operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
operands[5] = gen_rtx_REG (HImode, HARD_X_REGNUM);
}
")
 
(define_insn "addsi_ashift16"
[(set (match_operand:SI 0 "register_operand" "=D")
(plus:SI
(mult:SI (match_operand:SI 2 "general_operand" "uim")
(const_int 65536))
(match_operand:SI 1 "general_operand" "0")))
(clobber (match_scratch:HI 3 "=X"))]
"0"
"#")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI
(mult:SI (match_operand:SI 2 "general_operand" "")
(const_int 65536))
(match_operand:SI 1 "general_operand" "")))
(clobber (match_scratch:HI 3 "=X"))]
"0 && reload_completed && z_replacement_completed == 2"
[(set (reg:HI X_REGNUM) (plus:HI (reg:HI X_REGNUM) (match_dup 4)))]
"
{
operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
}")
 
(define_insn_and_split "addsi_andshr16"
[(set (match_operand:SI 0 "register_operand" "=D")
(plus:SI (and:SI (match_operand:SI 1 "general_operand" "%uim")
(const_int 65535))
(match_operand:SI 2 "general_operand" "0")))]
""
"#"
"z_replacement_completed == 2"
[(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
(set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM) (const_int 0)) (reg:HI CC_REGNUM)))]
"operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")
 
;;
;; 32-bit shifts are made by a small library routine that uses
;; a specific passing convention for parameters (for efficiency reasons).
;;
;; [D + X] -> Value to be shifted
;; Y -> Shift count
;;
;; The shift count is clobbered by the routine.
;;
(define_expand "ashlsi3"
[(parallel
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "general_operand" ""))
(clobber (scratch:HI))])
(parallel
[(set (match_dup 0) (ashift:SI (match_dup 0)
(match_operand:HI 2 "nonmemory_operand" "")))
(clobber (scratch:HI))])]
""
"")
 
(define_split
[(set (match_operand:SI 0 "nonimmediate_operand" "")
(ashift:SI (match_operand:SI 1 "general_operand" "")
(const_int 16)))
(clobber (match_scratch:HI 3 ""))]
""
[(set (match_dup 2) (match_dup 3))
(set (match_dup 4) (const_int 0))]
"operands[2] = m68hc11_gen_highpart (HImode, operands[0]);
operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")
 
(define_insn "*ashlsi3_const16"
[(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,*u")
(ashift:SI (match_operand:SI 1 "general_operand" "Duim,D,D")
(const_int 16)))
(clobber (match_scratch:HI 2 "=X,X,X"))]
""
"#")
 
(define_insn_and_split "*ashlsi3_const16_zexthi"
[(set (match_operand:SI 0 "nonimmediate_operand" "=D")
(ashift:SI (zero_extend:HI
(match_operand:HI 1 "general_operand" "duim*A"))
(const_int 16)))
(clobber (match_scratch:HI 2 "=X"))]
""
"#"
"reload_completed"
[(set (reg:HI X_REGNUM) (match_dup 1))
(set (reg:HI D_REGNUM) (const_int 0))]
"")
 
(define_insn "*ashlsi3_const1"
[(set (match_operand:SI 0 "non_push_operand" "=D,D,D,m,*u,*u")
(ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,m,*u,m,*u,m")
(const_int 1)))
(clobber (match_scratch:HI 2 "=X,X,X,&d,&d,&d"))]
""
"*
{
CC_STATUS_INIT;
if (X_REG_P (operands[1]))
{
return \"lsld\\n\\txgdx\\n\\trolb\\n\\trola\\n\\txgdx\";
}
else
{
rtx ops[2];
 
ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"lsld\", ops);
if (!X_REG_P (operands[0]))
{
ops[1] = ops[0];
ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
m68hc11_gen_movhi (insn, ops);
ops[0] = ops[1];
ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
m68hc11_gen_movhi (insn, ops);
}
else
{
/* Load the high part in X in case the source operand
uses X as a memory pointer. */
ops[0] = gen_rtx_REG (HImode, HARD_X_REGNUM);
ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"xgdx\", ops);
}
output_asm_insn (\"rolb\", ops);
output_asm_insn (\"rola\", ops);
if (!X_REG_P (operands[0]))
{
ops[1] = ops[0];
ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
m68hc11_gen_movhi (insn, ops);
}
else
{
output_asm_insn (\"xgdx\", ops);
}
return \"\";
}
}")
 
(define_insn "*ashlsi3_const"
[(set (match_operand:SI 0 "register_operand" "+D")
(ashift:SI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_scratch:HI 2 "=y"))]
"TARGET_M6811 /* See *ashlsi3 note. */"
"*
{
CC_STATUS_INIT;
return \"ldy\\t%1\\n\\tbsr\\t___ashlsi3\";
}")
 
(define_insn "*ashlsi3"
[(set (match_operand:SI 0 "register_operand" "+D,D")
(ashift:SI (match_dup 0)
(match_operand:HI 1 "general_operand" "y,mi")))
(clobber (match_scratch:HI 2 "=1,X"))]
""
"*
{
CC_STATUS_INIT;
 
/* There is a reload problem if we don't accept 'm' for the shift value.
A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
and this conflicts with all reloads. Since X, Y, Z are used there
is not enough register in class A_REGS.
 
Assuming that 'operands[1]' does not refer to the stack (which
is true for 68hc11 only, we save temporary the value of Y.
 
For 68HC12 we must also accept a constant because Z register is
disabled when compiling with -fomit-frame-pointer. We can come up
with a reload problem and the *lshrsi3_const pattern was disabled
for that reason. */
if (!Y_REG_P (operands[2]))
{
rtx ops[1];
int y_dead = dead_register_here (insn, iy_reg);
 
ops[0] = operands[1];
if (y_dead == 0)
{
output_asm_insn (\"pshy\", operands);
if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
}
output_asm_insn (\"ldy\\t%0\", ops);
output_asm_insn (\"bsr\\t___ashlsi3\", operands);
return y_dead == 0 ? \"puly\" : \"\";
}
return \"bsr\\t___ashlsi3\";
}")
 
(define_expand "ashlhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ashift:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT)
{
rtx scratch = gen_reg_rtx (HImode);
emit_move_insn (scratch, operands[2]);
emit_insn (gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_ASHIFT (HImode,
operand1, scratch)),
gen_rtx_CLOBBER (VOIDmode, scratch))));
DONE;
}
}")
 
(define_insn "*ashlhi3_const1"
[(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
(ashift:HI (match_operand:HI 1 "non_push_operand" "0,0")
(const_int 1)))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
if (D_REG_P (operands[0]))
{
return \"asld\";
}
output_asm_insn (\"asl\\t%b0\", operands);
output_asm_insn (\"rol\\t%h0\", operands);
CC_STATUS_INIT;
return \"\";
}")
 
 
(define_insn "*ashlhi3_2"
[(set (match_operand:HI 0 "register_operand" "=d,*x")
(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "register_operand" "+x,+d")))
(clobber (match_dup 2))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
return \"bsr\\t___lshlhi3\";
}")
 
(define_insn "*ashlhi3"
[(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
(ashift:HI (match_dup 0)
(match_operand:HI 1 "register_operand" "+x")))
(clobber (match_dup 1))]
""
"*
{
CC_STATUS_INIT;
return \"bsr\\t___lshlhi3\";
}")
 
(define_insn "*ashlhi3"
[(set (match_operand:HI 0 "register_operand" "=d,!*A")
(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "const_int_operand" "")))]
""
"*
{
int i;
 
if (A_REG_P (operands[0]))
return \"#\";
 
i = INTVAL (operands[2]);
if (i >= 8)
{
CC_STATUS_INIT;
output_asm_insn (\"tba\", operands);
if (i == 15)
{
output_asm_insn (\"rora\", operands);
output_asm_insn (\"anda\\t#0\", operands);
output_asm_insn (\"rora\", operands);
}
else
while (i != 8 )
{
output_asm_insn (\"asla\", operands);
i--;
}
return \"clrb\";
}
for (i = 0; i < INTVAL (operands[2]) - 1; i++)
{
output_asm_insn (\"asld\", operands);
}
return \"asld\";
}")
 
(define_expand "ashlqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ashift:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))]
""
"")
 
(define_insn "*ashlqi3_const1"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
(ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
(const_int 1)))]
""
"@
aslb
asl\\t%b0
asl\\t%b0
asl%0
#")
 
(define_insn "*ashlqi3_const"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "const_int_operand" "")))]
""
"*
{
int i;
const char* insn_code;
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
insn_code = \"aslb\";
else if (DA_REG_P (operands[0]))
insn_code = \"asla\";
else
return \"#\";
 
i = INTVAL (operands[2]);
if (i >= 8)
{
if (DA_REG_P (operands[0]))
return \"clra\";
else
return \"clrb\";
}
else if (i == 7)
{
if (DA_REG_P (operands[0]))
{
output_asm_insn (\"rora\", operands);
output_asm_insn (\"ldaa\\t#0\", operands);
return \"rora\";
}
else
{
output_asm_insn (\"rorb\", operands);
output_asm_insn (\"ldab\\t#0\", operands);
return \"rorb\";
}
}
else if (i == 6)
{
if (DA_REG_P (operands[0]))
{
output_asm_insn (\"rora\", operands);
output_asm_insn (\"rora\", operands);
output_asm_insn (\"rora\", operands);
return \"anda\\t#0xC0\";
}
else
{
output_asm_insn (\"rorb\", operands);
output_asm_insn (\"rorb\", operands);
output_asm_insn (\"rorb\", operands);
return \"andb\\t#0xC0\";
}
}
while (--i >= 0)
{
output_asm_insn (insn_code, operands);
}
return \"\";
}")
 
(define_insn "*ashlqi3"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "nonimmediate_operand"
"m*u*d*A,m*u*d*A,m*u")))]
""
"*
{
rtx ops[2];
 
if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
return \"#\";
 
ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
 
CC_STATUS_INIT;
return \"bsr\\t___lshlqi3\";
}")
 
(define_expand "ashrhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT)
{
rtx scratch = gen_reg_rtx (HImode);
 
emit_move_insn (scratch, operands[2]);
emit_insn (gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_ASHIFTRT (HImode,
operand1, scratch)),
gen_rtx_CLOBBER (VOIDmode, scratch))));
DONE;
}
}")
 
(define_insn "*ashrhi3_const1"
[(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
(ashiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
(const_int 1)))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
if (D_REG_P (operands[0]))
{
return \"asra\\n\\trorb\";
}
output_asm_insn (\"asr\\t%h0\", operands);
output_asm_insn (\"ror\\t%b0\", operands);
return \"\";
}")
 
 
(define_insn "*ashrhi3_const"
[(set (match_operand:HI 0 "register_operand" "=d,!*A")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "const_int_operand" "")))]
""
"*
{
rtx ops[2];
int val = INTVAL (operands[2]);
 
if (A_REG_P (operands[0]))
return \"#\";
 
if (val >= 15)
{
ops[0] = gen_label_rtx ();
 
output_asm_insn (\"clrb\", operands);
output_asm_insn (\"rola\", operands);
 
/* Clear A without clearing the carry flag. */
output_asm_insn (\"tba\", operands);
output_asm_insn (\"bcc\\t%l0\", ops);
output_asm_insn (\"coma\", operands);
output_asm_insn (\"comb\", operands);
 
CC_STATUS_INIT;
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
return \"\";
}
if (val >= 8)
{
ops[0] = gen_label_rtx ();
 
output_asm_insn (\"tab\", operands);
output_asm_insn (\"clra\", operands);
output_asm_insn (\"tstb\", operands);
output_asm_insn (\"bge\\t%l0\", ops);
output_asm_insn (\"deca\", operands);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
 
val -= 8;
 
while (val > 0)
{
output_asm_insn (\"asrb\", operands);
val--;
}
/* Status is ok. */
return \"\";
}
if (val == 7)
{
ops[0] = gen_label_rtx ();
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rola\", operands);
output_asm_insn (\"tab\", operands);
output_asm_insn (\"anda\\t#0\", operands);
output_asm_insn (\"bcc\\t%l0\", ops);
output_asm_insn (\"coma\", ops);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
return \"\";
}
while (val > 0)
{
output_asm_insn (\"asra\", operands);
output_asm_insn (\"rorb\", operands);
val--;
}
CC_STATUS_INIT;
 
return \"\";
}")
 
(define_insn "*ashrhi3"
[(set (match_operand:HI 0 "register_operand" "=d,*x")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "register_operand" "+x,+d")))
(clobber (match_dup 2))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
output_asm_insn (\"bsr\\t___ashrhi3\", operands);
return \"\";
}")
 
(define_expand "ashrsi3"
[(parallel
[(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
(clobber (scratch:HI))])
(parallel
[(set (match_operand:SI 0 "register_operand" "")
(ashiftrt:SI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))
(clobber (scratch:HI))])]
""
"")
 
(define_insn "*ashrsi3_const"
[(set (match_operand:SI 0 "register_operand" "+D")
(ashiftrt:SI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_scratch:HI 2 "=y"))]
"TARGET_M6811 /* See *ashrsi3 note. */"
"*
{
CC_STATUS_INIT;
return \"ldy\\t%1\\n\\tbsr\\t___ashrsi3\";
}")
 
(define_insn "*ashrsi3"
[(set (match_operand:SI 0 "register_operand" "+D,D")
(ashiftrt:SI (match_dup 0)
(match_operand:HI 1 "general_operand" "y,mi")))
(clobber (match_scratch:HI 2 "=1,X"))]
""
"*
{
CC_STATUS_INIT;
/* There is a reload problem if we don't accept 'm' for the shift value.
A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
and this conflicts with all reloads. Since X, Y, Z are used there
is not enough register in class A_REGS.
 
Assuming that 'operands[1]' does not refer to the stack (which
is true for 68hc11 only, we save temporary the value of Y.
 
For 68HC12 we must also accept a constant because Z register is
disabled when compiling with -fomit-frame-pointer. We can come up
with a reload problem and the *lshrsi3_const pattern was disabled
for that reason. */
if (!Y_REG_P (operands[2]))
{
rtx ops[1];
int y_dead = dead_register_here (insn, iy_reg);
 
ops[0] = operands[1];
if (y_dead == 0)
{
output_asm_insn (\"pshy\", operands);
if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
}
output_asm_insn (\"ldy\\t%0\", ops);
output_asm_insn (\"bsr\\t___ashrsi3\", operands);
return y_dead == 0 ? \"puly\" : \"\";
}
return \"bsr\\t___ashrsi3\";
}")
 
(define_expand "ashrqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ashiftrt:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))]
""
"")
 
(define_insn "*ashrqi3_const1"
[(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
(ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
(const_int 1)))]
""
"@
asrb
asr\\t%b0
asr\\t%b0
asr%0
#")
 
(define_insn "*ashrqi3_const"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "const_int_operand" "")))]
""
"*
{
int i;
const char* insn_code;
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
insn_code = \"asrb\";
else if (DA_REG_P (operands[0]))
insn_code = \"asra\";
else
return \"#\";
 
i = INTVAL (operands[2]);
if (i > 8)
i = 8;
while (--i >= 0)
{
output_asm_insn (insn_code, operands);
}
return \"\";
}")
 
(define_insn "*ashrqi3"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "nonimmediate_operand"
"m*u*d*A,m*u*d*A,m*u")))]
""
"*
{
rtx ops[2];
 
if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
return \"#\";
 
ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
 
CC_STATUS_INIT;
return \"bsr\\t___ashrqi3\";
}")
 
;;--------------------------------------------------------------------
;; logical shift instructions
;;--------------------------------------------------------------------
(define_expand "lshrdi3"
[(parallel [(set (match_operand:DI 0 "general_operand" "")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" "")))
(clobber (match_scratch:HI 3 ""))])]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT
|| (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) < 48
&& INTVAL (operands[2]) != 1))
{
FAIL;
}
}")
 
(define_insn_and_split "*lshrdi3_const32"
[(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
(const_int 32)))
(clobber (match_scratch:HI 2 "=&A,d,d"))]
""
"#"
"reload_completed"
[(const_int 0)]
"m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
m68hc11_gen_highpart (SImode, operands[1]),
operands[2]);
m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
const0_rtx, operands[2]);
DONE;")
 
(define_insn "*lshrdi3_const63"
[(set (match_operand:DI 0 "nonimmediate_operand" "=m,u")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
(match_operand:DI 2 "const_int_operand" "")))
(clobber (match_scratch:HI 3 "=d,d"))]
"INTVAL (operands[2]) >= 48"
"#")
 
(define_split
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
(match_operand:DI 2 "const_int_operand" "")))
(clobber (match_scratch:HI 3 "=d"))]
"z_replacement_completed && INTVAL (operands[2]) >= 56"
[(set (reg:QI D_REGNUM) (match_dup 9))
(set (reg:QI D_REGNUM) (lshiftrt:QI (reg:QI D_REGNUM) (match_dup 8)))
(set (reg:HI D_REGNUM) (zero_extend:HI (reg:QI D_REGNUM)))
(set (match_dup 4) (reg:HI D_REGNUM))
(set (reg:QI D_REGNUM) (const_int 0))
(set (match_dup 5) (reg:HI D_REGNUM))
(set (match_dup 6) (reg:HI D_REGNUM))
(set (match_dup 7) (reg:HI D_REGNUM))]
"operands[8] = GEN_INT (INTVAL (operands[2]) - 56);
operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
 
operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
operands[9] = m68hc11_gen_highpart (HImode, operands[9]);
operands[9] = m68hc11_gen_highpart (QImode, operands[9]);
 
operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")
 
(define_split
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
(match_operand:DI 2 "const_int_operand" "")))
(clobber (match_scratch:HI 3 "=d"))]
"z_replacement_completed && INTVAL (operands[2]) >= 48
&& INTVAL (operands[2]) < 56"
[(set (reg:HI D_REGNUM) (match_dup 9))
(set (reg:HI D_REGNUM) (lshiftrt:HI (reg:HI D_REGNUM) (match_dup 8)))
(set (match_dup 4) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (const_int 0))
(set (match_dup 5) (reg:HI D_REGNUM))
(set (match_dup 6) (reg:HI D_REGNUM))
(set (match_dup 7) (reg:HI D_REGNUM))]
"operands[8] = GEN_INT (INTVAL (operands[2]) - 48);
operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);
 
operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
operands[9] = m68hc11_gen_highpart (HImode, operands[1]);
operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")
 
(define_insn_and_split "*lshrdi_const1"
[(set (match_operand:DI 0 "non_push_operand" "=m,u")
(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
(const_int 1)))
(clobber (match_scratch:HI 2 "=d,d"))]
""
"#"
"z_replacement_completed == 2"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 2) (lshiftrt:HI (match_dup 2) (const_int 1)))
(set (match_dup 4) (match_dup 2))
 
(set (match_dup 2) (match_dup 5))
(parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 6) (match_dup 2))
 
(set (match_dup 2) (match_dup 7))
(parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 8) (match_dup 2))
 
(set (match_dup 2) (match_dup 9))
(parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 10) (match_dup 2))]
"operands[3] = m68hc11_gen_highpart (SImode, operands[1]);
operands[5] = m68hc11_gen_lowpart (HImode, operands[3]);
operands[3] = m68hc11_gen_highpart (HImode, operands[3]);
 
operands[4] = m68hc11_gen_highpart (SImode, operands[0]);
operands[6] = m68hc11_gen_lowpart (HImode, operands[4]);
operands[4] = m68hc11_gen_highpart (HImode, operands[4]);
 
operands[7] = m68hc11_gen_lowpart (SImode, operands[1]);
operands[9] = m68hc11_gen_lowpart (HImode, operands[7]);
operands[7] = m68hc11_gen_highpart (HImode, operands[7]);
 
operands[8] = m68hc11_gen_lowpart (SImode, operands[0]);
operands[10] = m68hc11_gen_lowpart (HImode, operands[8]);
operands[8] = m68hc11_gen_highpart (HImode, operands[8]);")
 
(define_expand "lshrsi3"
[(parallel
[(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
(clobber (scratch:HI))])
(parallel
[(set (match_operand:SI 0 "register_operand" "")
(lshiftrt:SI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))
(clobber (scratch:HI))])]
""
"")
 
(define_split
[(set (match_operand:SI 0 "non_push_operand" "")
(lshiftrt:SI (match_operand:SI 1 "general_operand" "")
(const_int 16)))
(clobber (match_scratch:HI 3 ""))]
"reload_completed && !(X_REG_P (operands[0]) && X_REG_P (operands[1]))"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 4) (const_int 0))]
"operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")
 
(define_insn "*lshrsi3_const16"
[(set (match_operand:SI 0 "non_push_operand" "=D,D,m,u")
(lshiftrt:SI (match_operand:SI 1 "general_operand" "uim,0,D,D")
(const_int 16)))
(clobber (match_scratch:HI 2 "=X,X,X,X"))]
""
"@
#
xgdx\\n\\tldx\\t#0
#
#")
 
(define_insn "*lshrsi3_const1"
[(set (match_operand:SI 0 "non_push_operand" "=D,D,D,m,*u,*u")
(lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0,m,*u,m,*u,m")
(const_int 1)))
(clobber (match_scratch:HI 2 "=X,X,X,&d,&d,&d"))]
""
"*
{
CC_STATUS_INIT;
if (X_REG_P (operands[1]))
{
return \"xgdx\\n\\tlsrd\\n\\txgdx\\n\\trora\\n\\trorb\";
}
else
{
rtx ops[2];
 
ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
ops[0] = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"lsrd\", ops);
if (!X_REG_P (operands[0]))
{
ops[1] = ops[0];
ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
m68hc11_gen_movhi (insn, ops);
ops[0] = ops[1];
ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
m68hc11_gen_movhi (insn, ops);
}
else
{
/* Load the lowpart in X in case the operands is some N,x. */
ops[0] = gen_rtx_REG (HImode, HARD_X_REGNUM);
ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"xgdx\", ops);
}
output_asm_insn (\"rora\", ops);
output_asm_insn (\"rorb\", ops);
if (!X_REG_P (operands[0]))
{
ops[1] = ops[0];
ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
m68hc11_gen_movhi (insn, ops);
}
return \"\";
}
}")
 
(define_insn "*lshrsi3_const"
[(set (match_operand:SI 0 "register_operand" "+D")
(lshiftrt:SI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_scratch:HI 2 "=y"))]
"TARGET_M6811 /* See *lshrsi3 note. */"
"*
{
CC_STATUS_INIT;
return \"ldy\\t%1\\n\\tbsr\\t___lshrsi3\";
}")
 
(define_insn "*lshrsi3"
[(set (match_operand:SI 0 "register_operand" "+D,D")
(lshiftrt:SI (match_dup 0)
(match_operand:HI 1 "general_operand" "y,mi")))
(clobber (match_scratch:HI 2 "=1,X"))]
""
"*
{
CC_STATUS_INIT;
/* There is a reload problem if we don't accept 'm' for the shift value.
A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
and this conflicts with all reloads. Since X, Y, Z are used there
is not enough register in class A_REGS.
 
Assuming that 'operands[1]' does not refer to the stack (which
is true for 68hc11 only, we save temporary the value of Y.
 
For 68HC12 we must also accept a constant because Z register is
disabled when compiling with -fomit-frame-pointer. We can come up
with a reload problem and the *lshrsi3_const pattern was disabled
for that reason. */
if (!Y_REG_P (operands[2]))
{
rtx ops[1];
int y_dead = dead_register_here (insn, iy_reg);
 
ops[0] = operands[1];
if (y_dead == 0)
{
output_asm_insn (\"pshy\", operands);
if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
}
output_asm_insn (\"ldy\\t%0\", ops);
output_asm_insn (\"bsr\\t___lshrsi3\", operands);
return y_dead == 0 ? \"puly\" : \"\";
}
return \"bsr\\t___lshrsi3\";
}")
 
(define_expand "lshrhi3"
[(set (match_operand:HI 0 "register_operand" "")
(lshiftrt:HI (match_operand:HI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT)
{
rtx scratch = gen_reg_rtx (HImode);
operand1 = force_reg (HImode, operand1);
 
emit_move_insn (scratch, operands[2]);
emit_insn (gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_LSHIFTRT (HImode,
operand1, scratch)),
gen_rtx_CLOBBER (VOIDmode, scratch))));
DONE;
}
}")
 
(define_insn "lshrhi3_const1"
[(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
(lshiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
(const_int 1)))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
if (D_REG_P (operands[0]))
return \"lsrd\";
 
CC_STATUS_INIT;
return \"lsr\\t%h0\\n\\tror\\t%b0\";
}")
 
(define_insn "lshrhi3_const"
[(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,!*A,!*A")
(lshiftrt:HI (match_operand:HI 1 "general_operand" "dm*A,!u,dm,!u")
(match_operand:HI 2 "const_int_operand" "i,i,i,i")))]
""
"*
{
int val = INTVAL (operands[2]);
 
if (A_REG_P (operands[0]))
return \"#\";
 
if (val >= 8)
{
if (val == 8)
CC_STATUS_INIT;
 
if (!H_REG_P (operands[1]))
{
output_asm_insn (\"clra\", operands);
output_asm_insn (\"ldab\\t%h1\", operands);
}
else if (A_REG_P (operands[1]))
{
output_asm_insn (\"st%1\\t%t0\", operands);
output_asm_insn (\"ldab\\t%t0\", operands);
output_asm_insn (\"clra\", operands);
}
else
{
output_asm_insn (\"tab\", operands);
output_asm_insn (\"clra\", operands);
}
val -= 8;
switch (val)
{
case 7:
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"tab\", operands);
output_asm_insn (\"rolb\", operands);
break;
 
case 6:
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"andb\\t#3\", operands);
break;
 
default:
while (val > 0)
{
val --;
output_asm_insn (\"lsrb\", operands);
}
break;
}
return \"\";
}
 
if (!D_REG_P (operands[1]))
m68hc11_gen_movhi (insn, operands);
switch (val)
{
case 7:
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"tab\", operands);
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rola\", operands);
output_asm_insn (\"rola\", operands);
output_asm_insn (\"anda\\t#1\", operands);
CC_STATUS_INIT;
break;
 
default:
while (val > 0)
{
val --;
output_asm_insn (\"lsrd\", operands);
}
}
return \"\";
}")
 
(define_insn "*lshrhi3"
[(set (match_operand:HI 0 "register_operand" "=d,*x")
(lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "register_operand" "+x,+d")))
(clobber (match_dup 2))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
return \"bsr\\t___lshrhi3\";
}")
 
(define_expand "lshrqi3"
[(set (match_operand:QI 0 "register_operand" "")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "general_operand" "")))]
""
"")
 
(define_insn "*lshrqi3_const1"
[(set (match_operand:QI 0 "nonimmediate_operand" "=m,d,!u,!*q,!*A")
(lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
(const_int 1)))]
""
"@
lsr\\t%b0
lsrb
lsr\\t%b0
lsr%0
#")
 
(define_insn "*lshrqi3_const"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "const_int_operand" "")))]
""
"*
{
int i;
const char* insn_code;
 
if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
insn_code = \"lsrb\";
else if (DA_REG_P (operands[0]))
insn_code = \"lsra\";
else
return \"#\";
 
i = INTVAL (operands[2]);
if (i >= 8)
{
if (DA_REG_P (operands[0]))
return \"clra\";
else
return \"clrb\";
}
else if (i == 7)
{
if (DA_REG_P (operands[0]))
{
output_asm_insn (\"rola\", operands);
output_asm_insn (\"ldaa\\t#0\", operands);
return \"rola\";
}
else
{
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"ldab\\t#0\", operands);
return \"rolb\";
}
}
else if (i == 6)
{
if (DA_REG_P (operands[0]))
{
output_asm_insn (\"rola\", operands);
output_asm_insn (\"rola\", operands);
output_asm_insn (\"rola\", operands);
return \"anda\\t#3\";
}
else
{
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rolb\", operands);
output_asm_insn (\"rolb\", operands);
return \"andb\\t#3\";
}
}
while (--i >= 0)
{
output_asm_insn (insn_code, operands);
}
return \"\";
}")
 
(define_insn "*lshrqi3"
[(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
(match_operand:QI 2 "nonimmediate_operand"
"m*u*d*A,m*u*d*A,m*u")))]
""
"*
{
rtx ops[2];
 
if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
ops[0] = gen_rtx_REG (QImode, HARD_A_REGNUM);
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
 
if (!optimize || optimize_size)
{
return \"bsr\\t___lshrqi3\";
}
 
ops[0] = gen_label_rtx ();
ops[1] = gen_label_rtx ();
output_asm_insn (\"ble\\t%l1\", ops);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[0]));
 
output_asm_insn (\"lsrb\", operands);
output_asm_insn (\"deca\", operands);
output_asm_insn (\"bne\\t%l0\", ops);
 
(*targetm.asm_out.internal_label) (asm_out_file, \"L\",
CODE_LABEL_NUMBER (ops[1]));
return \"\";
}")
 
(define_insn "*rotlqi3_with_carry"
[(set (match_operand:QI 0 "register_operand" "=d,!q")
(rotate:QI (match_operand:QI 1 "register_operand" "0,0")
(reg:QI CC_REGNUM)))]
""
"*
{
if (DA_REG_P (operands[0]))
return \"rola\";
else
return \"rolb\";
}")
 
(define_insn "*rotlhi3_with_carry"
[(set (match_operand:HI 0 "register_operand" "=d")
(rotate:HI (match_operand:HI 1 "register_operand" "0")
(const_int 1)))
(clobber (reg:HI CC_REGNUM))]
""
"*
{
CC_STATUS_INIT;
return \"rolb\\n\\trola\";
}")
 
(define_insn "*rotrhi3_with_carry"
[(set (match_operand:HI 0 "register_operand" "=d")
(rotatert:HI (match_operand:HI 1 "register_operand" "0")
(const_int 1)))
(clobber (reg:HI CC_REGNUM))]
""
"*
{
CC_STATUS_INIT;
return \"rora\\n\\trorb\";
}")
 
(define_insn "rotlqi3"
[(set (match_operand:QI 0 "register_operand" "=d,!q")
(rotate:QI (match_operand:QI 1 "register_operand" "0,0")
(match_operand:QI 2 "const_int_operand" "i,i")))]
""
"*
{
m68hc11_gen_rotate (ROTATE, insn, operands);
return \"\";
}")
 
(define_insn "rotrqi3"
[(set (match_operand:QI 0 "register_operand" "=d,!q")
(rotatert:QI (match_operand:QI 1 "register_operand" "0,0")
(match_operand:QI 2 "const_int_operand" "i,i")))]
""
"*
{
m68hc11_gen_rotate (ROTATERT, insn, operands);
return \"\";
}")
 
(define_expand "rotlhi3"
[(set (match_operand:HI 0 "register_operand" "")
(rotate:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT)
{
rtx scratch = gen_reg_rtx (HImode);
operand1 = force_reg (HImode, operand1);
 
emit_move_insn (scratch, operands[2]);
emit_insn (gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_ROTATE (HImode,
operand1, scratch)),
gen_rtx_CLOBBER (VOIDmode, scratch))));
DONE;
}
}")
 
(define_insn "rotlhi3_const"
[(set (match_operand:HI 0 "register_operand" "=d")
(rotate:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "const_int_operand" "i")))]
""
"*
{
m68hc11_gen_rotate (ROTATE, insn, operands);
return \"\";
}")
 
(define_insn "*rotlhi3"
[(set (match_operand:HI 0 "register_operand" "=d,*x")
(rotate:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "+x,+d")))
(clobber (match_dup 2))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
return \"bsr\\t___rotlhi3\";
}")
 
(define_expand "rotrhi3"
[(set (match_operand:HI 0 "register_operand" "")
(rotatert:HI (match_operand:HI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" "")))]
""
"
{
if (GET_CODE (operands[2]) != CONST_INT)
{
rtx scratch = gen_reg_rtx (HImode);
operand1 = force_reg (HImode, operand1);
 
emit_move_insn (scratch, operands[2]);
emit_insn (gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_ROTATERT (HImode,
operand1, scratch)),
gen_rtx_CLOBBER (VOIDmode, scratch))));
DONE;
}
}")
 
(define_insn "rotrhi3_const"
[(set (match_operand:HI 0 "register_operand" "=d")
(rotatert:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "const_int_operand" "i")))]
""
"*
{
m68hc11_gen_rotate (ROTATERT, insn, operands);
return \"\";
}")
 
(define_insn "*rotrhi3"
[(set (match_operand:HI 0 "register_operand" "=d,*x")
(rotatert:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "+x,+d")))
(clobber (match_dup 2))]
""
"*
{
if (A_REG_P (operands[0]))
return \"#\";
 
return \"bsr\\t___rotrhi3\";
}")
 
;; Split a shift operation on an address register in a shift
;; on D_REGNUM.
(define_split /* "*rotrhi3_addr" */
[(set (match_operand:HI 0 "hard_addr_reg_operand" "")
(match_operator:HI 3 "m68hc11_shift_operator"
[(match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "register_operand" "")]))
(clobber (match_dup 2))]
"z_replacement_completed == 2"
[(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])
(parallel [(set (reg:HI D_REGNUM)
(match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 0)]))
(clobber (match_dup 0))])
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"")
 
;;--------------------------------------------------------------------
;;- 68HC12 Decrement/Increment and branch
;;--------------------------------------------------------------------
;; These patterns are used by loop optimization as well as peephole2
;; They must handle reloading themselves and the scratch register
;; is used for that. Even if we accept memory operand, we must not
;; accept them on the predicate because it might create too many reloads.
;; (specially on HC12 due to its auto-incdec addressing modes).
;;
(define_expand "decrement_and_branch_until_zero"
[(parallel [(set (pc)
(if_then_else
(ne (plus:HI (match_operand:HI 0 "register_operand" "")
(const_int 0))
(const_int 1))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0)
(const_int -1)))
(clobber (match_scratch:HI 2 ""))])]
"TARGET_M6812"
"")
 
(define_expand "doloop_end"
[(use (match_operand 0 "" "")) ; loop pseudo
(use (match_operand 1 "" "")) ; iterations; zero if unknown
(use (match_operand 2 "" "")) ; max iterations
(use (match_operand 3 "" "")) ; loop level
(use (match_operand 4 "" ""))] ; label
"TARGET_M6812"
"
{
/* Reject non-constant loops as it generates bigger code due to
the handling of the loop register. We can do better by using
the peephole2 dbcc/ibcc patterns. */
if (INTVAL (operands[1]) == 0)
{
FAIL;
}
 
/* Note that for xxx_dbcc_dec_yy the gen_rtx_NE is only used to pass
the operator and its operands are not relevant. */
if (GET_MODE (operands[0]) == HImode)
{
emit_jump_insn (gen_m68hc12_dbcc_dec_hi (operands[0],
gen_rtx_NE (HImode,
operands[0],
const1_rtx),
operands[4]));
DONE;
}
if (GET_MODE (operands[0]) == QImode)
{
emit_jump_insn (gen_m68hc12_dbcc_dec_qi (operands[0],
gen_rtx_NE (QImode,
operands[0],
const1_rtx),
operands[4]));
DONE;
}
 
FAIL;
}")
 
;; Decrement-and-branch insns.
(define_insn "m68hc12_dbcc_dec_hi"
[(set (pc)
(if_then_else
(match_operator 1 "m68hc11_eq_compare_operator"
[(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
(const_int 1)])
(label_ref (match_operand 2 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0) (const_int -1)))
(clobber (match_scratch:HI 3 "=X,dxy"))]
"TARGET_M6812"
"*
{
if (!H_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
if (GET_CODE (operands[1]) == EQ)
return \"dbeq\\t%0,%l2\";
else
return \"dbne\\t%0,%l2\";
}")
 
;; Decrement-and-branch insns.
(define_insn "m68hc12_dbcc_inc_hi"
[(set (pc)
(if_then_else
(match_operator 1 "m68hc11_eq_compare_operator"
[(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
(const_int -1)])
(label_ref (match_operand 2 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0) (const_int 1)))
(clobber (match_scratch:HI 3 "=X,dxy"))]
"TARGET_M6812"
"*
{
if (!H_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
if (GET_CODE (operands[1]) == EQ)
return \"ibeq\\t%0,%l2\";
else
return \"ibeq\\t%0,%l2\";
}")
 
;; Decrement-and-branch (QImode).
(define_insn "m68hc12_dbcc_dec_qi"
[(set (pc)
(if_then_else
(match_operator 1 "m68hc11_eq_compare_operator"
[(match_operand:QI 0 "register_operand" "+d,m*u*A")
(const_int 1)])
(label_ref (match_operand 2 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0) (const_int -1)))
(clobber (match_scratch:QI 3 "=X,d"))]
"TARGET_M6812"
"*
{
if (!D_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
if (GET_CODE (operands[1]) == EQ)
return \"dbeq\\tb,%l2\";
else
return \"dbne\\tb,%l2\";
}")
 
;; Increment-and-branch (QImode).
(define_insn "m68hc12_dbcc_inc_qi"
[(set (pc)
(if_then_else
(match_operator 1 "m68hc11_eq_compare_operator"
[(match_operand:QI 0 "register_operand" "+d,m*u*A")
(const_int -1)])
(label_ref (match_operand 2 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0) (const_int 1)))
(clobber (match_scratch:QI 3 "=X,d"))]
"TARGET_M6812"
"*
{
if (!D_REG_P (operands[0]))
return \"#\";
 
CC_STATUS_INIT;
if (GET_CODE (operands[1]) == EQ)
return \"ibeq\\tb,%l2\";
else
return \"ibeq\\tb,%l2\";
}")
 
;; Split the above to handle the case where operand 0 is in memory
;; (a register that couldn't get a hard register)
(define_split
[(set (pc)
(if_then_else
(match_operator 3 "m68hc11_eq_compare_operator"
[(match_operand:HI 0 "general_operand" "")
(match_operand:HI 1 "const_int_operand" "")])
(label_ref (match_operand 4 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0) (match_operand 2 "const_int_operand" "")))
(clobber (match_operand:HI 5 "hard_reg_operand" ""))]
"TARGET_M6812 && reload_completed"
[(set (match_dup 5) (match_dup 0))
(set (match_dup 5) (plus:HI (match_dup 5) (match_dup 2)))
(set (match_dup 0) (match_dup 5))
(set (pc)
(if_then_else (match_op_dup 3
[(match_dup 5) (const_int 0)])
(label_ref (match_dup 4)) (pc)))]
"")
 
;; Split the above to handle the case where operand 0 is in memory
;; (a register that couldn't get a hard register)
(define_split
[(set (pc)
(if_then_else
(match_operator 3 "m68hc11_eq_compare_operator"
[(match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "const_int_operand" "")])
(label_ref (match_operand 4 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0) (match_operand 2 "const_int_operand" "")))
(clobber (match_operand:QI 5 "hard_reg_operand" ""))]
"TARGET_M6812 && reload_completed"
[(set (match_dup 5) (match_dup 0))
(set (match_dup 5) (plus:QI (match_dup 5) (match_dup 2)))
(set (match_dup 0) (match_dup 5))
(set (pc)
(if_then_else (match_op_dup 3
[(match_dup 5) (const_int 0)])
(label_ref (match_dup 4)) (pc)))]
"")
 
;;--------------------------------------------------------------------
;;- Jumps and transfers
;;--------------------------------------------------------------------
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"bra\\t%l0")
 
(define_expand "beq"
[(set (pc)
(if_then_else (eq (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (EQ, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bne"
[(set (pc)
(if_then_else (ne (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (NE, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bgt"
[(set (pc)
(if_then_else (gt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (GT, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bgtu"
[(set (pc)
(if_then_else (gtu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (GTU, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "blt"
[(set (pc)
(if_then_else (lt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (LT, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bltu"
[(set (pc)
(if_then_else (ltu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (LTU, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bge"
[(set (pc)
(if_then_else (ge (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (GE, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bgeu"
[(set (pc)
(if_then_else (geu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (GEU, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "ble"
[(set (pc)
(if_then_else (le (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (LE, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
(define_expand "bleu"
[(set (pc)
(if_then_else (leu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
m68hc11_expand_compare_and_branch (LEU, m68hc11_compare_op0,
m68hc11_compare_op1,
operands[0]);
DONE;
}")
 
;;
;; Test and branch instructions for 68HC12 for EQ and NE.
;; 'z' must not appear in the constraints because the z replacement
;; pass does not know how to restore the replacement register.
;;
(define_insn "*tbeq"
[(set (pc)
(if_then_else (eq (match_operand:HI 0 "register_operand" "dxy")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
"TARGET_M6812"
"*
{
/* If the flags are already set correctly, use 'bne/beq' which are
smaller and a little bit faster. This happens quite often due
to reloading of operands[0]. In that case, flags are set correctly
due to the load instruction. */
if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
|| (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
return \"beq\\t%l1\";
else
return \"tbeq\\t%0,%l1\";
}")
 
(define_insn "*tbne"
[(set (pc)
(if_then_else (ne (match_operand:HI 0 "register_operand" "dxy")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
"TARGET_M6812"
"*
{
if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
|| (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
return \"bne\\t%l1\";
else
return \"tbne\\t%0,%l1\";
}")
 
;;
;; Test and branch with 8-bit register. Register must be B (or A).
;;
(define_insn "*tbeq8"
[(set (pc)
(if_then_else (eq (match_operand:QI 0 "register_operand" "d")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
"TARGET_M6812"
"*
{
if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
|| (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
return \"beq\\t%l1\";
else
return \"tbeq\\tb,%l1\";
}")
 
(define_insn "*tbne8"
[(set (pc)
(if_then_else (ne (match_operand:QI 0 "register_operand" "d")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
"TARGET_M6812"
"*
{
if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
|| (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
return \"bne\\t%l1\";
else
return \"tbne\\tb,%l1\";
}")
 
(define_insn "*beq"
[(set (pc)
(if_then_else (eq (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"beq\\t%l0")
 
(define_insn "*bne"
[(set (pc)
(if_then_else (ne (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"bne\\t%l0")
 
(define_insn "*bgt"
[(set (pc)
(if_then_else (gt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"bgt\\t%l0")
 
(define_insn "*bgtu"
[(set (pc)
(if_then_else (gtu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"bhi\\t%l0")
 
(define_insn "*blt"
[(set (pc)
(if_then_else (lt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bmi\\t%l0\";
else
return \"blt\\t%l0\";
}")
 
(define_insn "*bltu"
[(set (pc)
(if_then_else (ltu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"blo\\t%l0")
 
(define_insn "*bge"
[(set (pc)
(if_then_else (ge (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bpl\\t%l0\";
else
return \"bge\\t%l0\";
}")
 
(define_insn "*bgeu"
[(set (pc)
(if_then_else (geu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"bhs\\t%l0")
 
(define_insn "*ble"
[(set (pc)
(if_then_else (le (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
else
return \"ble\\t%l0\";
}")
 
(define_insn "*bleu"
[(set (pc)
(if_then_else (leu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"bls\\t%l0")
 
;;--------------------------------------------------------------------
;;- Negative test and branch
;;--------------------------------------------------------------------
(define_insn ""
[(set (pc)
(if_then_else (eq (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"bne\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (ne (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"beq\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (gt (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
else
return \"ble\\t%l0\";
}")
 
(define_insn ""
[(set (pc)
(if_then_else (gtu (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"bls\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (lt (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bpl\\t%l0\";
else
return \"bge\\t%l0\";
}")
 
(define_insn ""
[(set (pc)
(if_then_else (ltu (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"bhs\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (ge (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"*
{
if (cc_prev_status.flags & CC_NO_OVERFLOW)
return \"bmi\\t%l0\";
else
return \"blt\\t%l0\";
}")
 
(define_insn ""
[(set (pc)
(if_then_else (geu (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"blo\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (le (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"bgt\\t%l0")
 
(define_insn ""
[(set (pc)
(if_then_else (leu (cc0)
(const_int 0))
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"bhi\\t%l0")
 
;;--------------------------------------------------------------------
;;- Calls
;;--------------------------------------------------------------------
;;
;;- Call a function that returns no value.
(define_insn "call"
[(call (match_operand:QI 0 "memory_operand" "m")
(match_operand:SI 1 "general_operand" "g"))]
;; Operand 1 not really used on the m68hc11.
""
"*
{
if (GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
{
if (m68hc11_is_far_symbol (operands[0]))
{
if (TARGET_M6812)
{
output_asm_insn (\"call\\t%0\", operands);
return \"\";
}
else
{
output_asm_insn (\"pshb\", operands);
output_asm_insn (\"ldab\\t#%%page(%0)\", operands);
output_asm_insn (\"ldy\\t#%%addr(%0)\", operands);
return \"jsr\\t__call_a32\";
}
}
if (m68hc11_is_trap_symbol (operands[0]))
return \"swi\";
else
return \"bsr\\t%0\";
}
else
{
return \"jsr\\t%0\";
}
}")
 
(define_insn "call_value"
[(set (match_operand 0 "" "=g")
(call (match_operand:QI 1 "memory_operand" "m")
(match_operand:SI 2 "general_operand" "g")))]
""
"*
{
if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
{
if (m68hc11_is_far_symbol (operands[1]))
{
if (TARGET_M6812)
{
output_asm_insn (\"call\\t%1\", operands);
return \"\";
}
else
{
output_asm_insn (\"pshb\", operands);
output_asm_insn (\"ldab\\t#%%page(%1)\", operands);
output_asm_insn (\"ldy\\t#%%addr(%1)\", operands);
return \"jsr\\t__call_a32\";
}
}
if (m68hc11_is_trap_symbol (operands[1]))
return \"swi\";
else
return \"bsr\\t%1\";
}
else
{
return \"jsr\\t%1\";
}
}")
 
;; Call subroutine returning any type.
 
(define_expand "untyped_call"
[(parallel [(call (match_operand 0 "" "")
(const_int 0))
(match_operand 1 "" "")
(match_operand 2 "" "")])]
""
"
{
int i;
 
emit_call_insn (gen_call (operands[0], const0_rtx));
 
for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
rtx set = XVECEXP (operands[2], 0, i);
emit_move_insn (SET_DEST (set), SET_SRC (set));
}
 
/* The optimizer does not know that the call sets the function value
registers we stored in the result block. We avoid problems by
claiming that all hard registers are used and clobbered at this
point. */
emit_insn (gen_blockage ());
 
DONE;
}")
 
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
 
(define_insn "blockage"
[(unspec_volatile [(const_int 0)] 0)]
""
"")
 
(define_insn "nop"
[(const_int 0)]
""
"nop")
(define_expand "prologue"
[(const_int 0)]
""
"
{
expand_prologue ();
DONE;
}")
 
(define_expand "epilogue"
[(return)]
""
"
{
expand_epilogue ();
DONE;
}")
 
;; Used for frameless functions which save no regs and allocate no locals.
(define_expand "return"
[(return)]
"reload_completed && m68hc11_total_frame_size () == 0"
"
{
int ret_size = 0;
 
if (current_function_return_rtx)
ret_size = GET_MODE_SIZE (GET_MODE (current_function_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 (current_function_return_rtx)
ret_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));
 
if (ret_size == 0)
return \"jmp\\t__return_void\";
if (ret_size <= 2)
return \"jmp\\t__return_16\";
if (ret_size <= 4)
return \"jmp\\t__return_32\";
return \"jmp\\t__return_16\";
}
}")
 
(define_insn "*return_16bit"
[(return)
(use (reg:HI D_REGNUM))]
"reload_completed && m68hc11_total_frame_size () == 0"
"*
{
rtx next = next_active_insn (insn);
 
if (next
&& GET_CODE (next) == JUMP_INSN
&& GET_CODE (PATTERN (next)) == RETURN)
return \"\";
if (current_function_interrupt || current_function_trap)
return \"rti\";
else if (!current_function_far)
return \"rts\";
else if (TARGET_M6812)
return \"rtc\";
else
return \"jmp\\t__return_16\";
}")
 
(define_insn "*return_32bit"
[(return)
(use (reg:SI 0))]
"reload_completed && m68hc11_total_frame_size () == 0"
"*
{
rtx next = next_active_insn (insn);
 
if (next
&& GET_CODE (next) == JUMP_INSN
&& GET_CODE (PATTERN (next)) == RETURN)
return \"\";
if (current_function_interrupt || current_function_trap)
return \"rti\";
else if (!current_function_far)
return \"rts\";
else if (TARGET_M6812)
return \"rtc\";
else
return \"jmp\\t__return_32\";
}")
 
(define_insn "indirect_jump"
[(set (pc) (match_operand:HI 0 "nonimmediate_operand" "xy"))]
""
"jmp\\t0,%0")
 
;;--------------------------------------------------------------------
;;- Table jump
;;--------------------------------------------------------------------
;;
;; Operand 0 is the address of the table element to use
;; operand 1 is the CODE_LABEL for the table
;;--------------------------------------------------------------------
(define_expand "tablejump"
[(parallel [(set (pc) (match_operand 0 "" ""))
(use (label_ref (match_operand 1 "" "")))])]
""
"")
 
(define_insn "*jump_indirect"
[(parallel [
(set (pc) (match_operand:HI 0 "register_operand" "xy"))
(use (label_ref (match_operand 1 "" "")))])]
""
"jmp\\t0,%0")
 
;;--------------------------------------------------------------------
;;- Peepholes
;;--------------------------------------------------------------------
 
;;--------------------------------------------------------------------
;;- 68HC12 dbcc/ibcc peepholes
;;--------------------------------------------------------------------
;;
;; Replace: "addd #-1; bne L1" into "dbne d,L1"
;; "addd #-1; beq L1" into "dbeq d,L1"
;; "addd #1; bne L1" into "ibne d,L1"
;; "addd #1; beq L1" into "ibeq d,L1"
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
[(match_dup 0)
(const_int 0)])
(label_ref (match_operand 3 "" "")) (pc)))]
"TARGET_M6812 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
[(parallel [
(set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
(label_ref (match_dup 3)) (pc)))
(set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))
(clobber (match_dup 4))])]
"operands[4] = gen_rtx_SCRATCH(HImode);
operands[5] = GEN_INT (-INTVAL (operands[1]));")
 
 
;;
;; Replace: "addb #-1; bne L1" into "dbne b,L1"
;; "addb #-1; beq L1" into "dbeq b,L1"
;;
(define_peephole2
[(set (match_operand:QI 0 "hard_reg_operand" "")
(plus:QI (match_dup 0)
(match_operand:QI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
[(match_dup 0)
(const_int 0)])
(label_ref (match_operand 3 "" "")) (pc)))]
"TARGET_M6812 && D_REG_P (operands[0])
&& (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
[(parallel [
(set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
(label_ref (match_dup 3)) (pc)))
(set (match_dup 0) (plus:QI (match_dup 0) (match_dup 1)))
(clobber (match_dup 4))])]
"operands[4] = gen_rtx_SCRATCH(QImode);
operands[5] = GEN_INT (-INTVAL (operands[1]));")
 
 
;;--------------------------------------------------------------------
;;- Move peephole2
;;--------------------------------------------------------------------
 
;;
;; Replace "leas 2,sp" with a "pulx" or a "puly".
;; On 68HC12, this is one cycle slower but one byte smaller.
;; pr target/6899: This peephole was not valid because a register CSE
;; pass removes the pulx/puly. The 'use' clause ensure that the pulx is
;; not removed.
;;
(define_peephole2
[(set (reg:HI SP_REGNUM) (plus:HI (reg:HI SP_REGNUM) (const_int 2)))
(match_scratch:HI 0 "xy")]
"TARGET_M6812 && optimize_size"
[(set (match_dup 0) (match_dup 1))
(use (match_dup 0))]
"operands[1] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));")
 
;; Replace: "pshx; tfr d,x; stx 0,sp" into "pshd; tfr d,x"
;;
;; PR 14542: emit a use to pretend we need the value of initial register.
;; Otherwise verify_local_live_at_start will die due to a live change
;; of that register.
;;
(define_peephole2
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(match_operand:HI 0 "hard_reg_operand" ""))
(set (match_dup 0)
(match_operand:HI 1 "hard_reg_operand" ""))
(set (mem:HI (reg:HI SP_REGNUM))
(match_dup 0))]
"TARGET_M6812"
[(use (match_dup 0))
(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(match_dup 1))
(set (match_dup 0) (match_dup 1))]
"")
 
;;
;; Change: "ldd 0,sp; pulx" into "puld"
;; This sequence usually appears at end a functions.
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(mem:HI (reg:HI SP_REGNUM)))
(use (match_dup 0))
(set (match_operand:HI 1 "hard_reg_operand" "")
(mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
"peep2_reg_dead_p (2, operands[1])"
[(set (match_dup 0) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))
(use (match_dup 0))]
"")
 
;; Replace: "pshx; clr 0,sp; clr 1,sp" by "clr 1,-sp; clr 1,-sp"
;; Appears to allocate local variables.
(define_peephole2
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(match_operand:HI 0 "hard_reg_operand" ""))
(set (mem:QI (plus:HI (reg:HI SP_REGNUM) (const_int 1)))
(const_int 0))
(set (mem:QI (reg:HI SP_REGNUM))
(const_int 0))]
"TARGET_M6812"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(const_int 0))]
"")
 
;; Likewise for HI mode
(define_peephole2
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(match_operand:HI 0 "hard_reg_operand" ""))
(set (mem:HI (reg:HI SP_REGNUM))
(const_int 0))]
"TARGET_M6812"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(const_int 0))]
"")
;;--------------------------------------------------------------------
;;-
;;--------------------------------------------------------------------
;;
;; Optimize memory<->memory moves when the value is also loaded in
;; a register.
;;
(define_peephole2
[(set (match_operand:QI 0 "memory_operand" "")
(match_operand:QI 1 "memory_operand" ""))
(set (reg:QI D_REGNUM)
(match_operand:QI 2 "memory_operand" ""))]
"(rtx_equal_p (operands[0], operands[2]) && !side_effects_p (operands[0]))
|| (GET_CODE (XEXP (operands[0], 0)) == REG
&& GET_CODE (XEXP (operands[2], 0)) == POST_INC
&& rtx_equal_p (XEXP (operands[0], 0), XEXP (XEXP (operands[2], 0), 0)))"
[(set (reg:QI D_REGNUM) (match_dup 1))
(set (match_dup 2) (reg:QI D_REGNUM))]
"")
 
;;
;; Remove a possible move before a compare instruction when that
;; move will go in a dead register. Compare with the source then.
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "hard_reg_operand" ""))
(set (cc0)
(compare (match_dup 0)
(match_operand:HI 2 "cmp_operand" "")))]
"(X_REG_P (operands[1]) || Y_REG_P (operands[1]))
&& peep2_reg_dead_p (2, operands[0])
&& !reg_mentioned_p (operands[0], operands[2])"
[(set (cc0) (compare (match_dup 1) (match_dup 2)))]
"")
 
;;
;; Optimize loading a constant to memory when that same constant
;; is loaded to a hard register. Switch the two to use the register
;; for memory initialization. In most cases, the constant is 0.
;;
(define_peephole2
[(set (match_operand:HI 0 "memory_operand" "")
(match_operand:HI 1 "immediate_operand" ""))
(set (match_operand:HI 2 "hard_reg_operand" "")
(match_dup 1))]
"(D_REG_P (operands[2]) || X_REG_P (operands[2]) || Y_REG_P (operands[2]))
&& !reg_mentioned_p (operands[2], operands[0])"
[(set (match_dup 2) (match_dup 1))
(set (match_dup 0) (match_dup 2))]
"")
 
;;
;; Reorganize to optimize address computations.
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))]
"(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
"")
 
;;
;; Replace: "ldx #N; xgdx; addd <var>; xgdx" by "ldab #N; ldx <var>; abx"
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))
(match_scratch:QI 3 "d")]
"TARGET_M6811 && (INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 0x0ff)"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (plus:HI (zero_extend:HI (match_dup 3)) (match_dup 0)))]
"operands[4] = m68hc11_gen_lowpart (QImode, operands[1]);")
 
;;
;; Replace: "ldx #N; xgdx; addd <var>; xgdx" by "ldab #N; ldx <var>; abx"
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))]
"TARGET_M6812"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
"")
 
;;
;; Optimize an address register increment and a compare to use
;; a PRE_INC or PRE_DEC addressing mode (disabled on the tst insn
;; before reload, but can be enabled after).
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(set (cc0)
(match_operand:QI 2 "memory_operand" ""))]
"TARGET_AUTO_INC_DEC
&& (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
&& reg_mentioned_p (operands[0], operands[2])"
[(set (cc0) (match_dup 3))]
"if (INTVAL (operands[1]) == 1)
operands[3] = gen_rtx_MEM (QImode,
gen_rtx_PRE_INC (HImode, operands[0]));
else
operands[3] = gen_rtx_MEM (QImode,
gen_rtx_PRE_DEC (HImode, operands[0]));
")
 
;;
;; Likewise for compare.
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(set (cc0)
(compare (match_operand:QI 2 "hard_reg_operand" "")
(match_operand:QI 3 "memory_operand" "")))]
"TARGET_AUTO_INC_DEC
&& (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
&& reg_mentioned_p (operands[0], operands[3])"
[(set (cc0) (compare (match_dup 2) (match_dup 4)))]
"if (INTVAL (operands[1]) == 1)
operands[4] = gen_rtx_MEM (QImode,
gen_rtx_PRE_INC (HImode, operands[0]));
else
operands[4] = gen_rtx_MEM (QImode,
gen_rtx_PRE_DEC (HImode, operands[0]));
")
 
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(set (cc0)
(compare (match_operand:QI 2 "memory_operand" "")
(match_operand:QI 3 "hard_reg_operand" "")))]
"TARGET_AUTO_INC_DEC
&& (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
&& reg_mentioned_p (operands[0], operands[2])"
[(set (cc0) (compare (match_dup 4) (match_dup 3)))]
"if (INTVAL (operands[1]) == 1)
operands[4] = gen_rtx_MEM (QImode,
gen_rtx_PRE_INC (HImode, operands[0]));
else
operands[4] = gen_rtx_MEM (QImode,
gen_rtx_PRE_DEC (HImode, operands[0]));
")
 
;;
;; Replace a "ldx #N; addx <sp>" with a "ldx <sp>; addx #n"
;; (avoids many temporary moves because we can't add sp to another reg easily)
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
""
[(set (match_dup 0) (reg:HI SP_REGNUM))
(set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
"")
 
;;
;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_operand:HI 2 "general_operand" "")))]
"(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
"")
 
;;
;;
;;
(define_peephole2
[(parallel
[(set (match_operand:SI 0 "hard_reg_operand" "")
(ashift:SI (match_operand:SI 1 "general_operand" "")
(const_int 1)))
(clobber (match_scratch:HI 2 ""))])
(set (match_operand:HI 3 "nonimmediate_operand" "") (reg:HI D_REGNUM))
(set (match_operand:HI 4 "nonimmediate_operand" "") (reg:HI X_REGNUM))]
"!X_REG_P (operands[1])
&& peep2_reg_dead_p (2, gen_rtx_REG (HImode, D_REGNUM))
&& peep2_reg_dead_p (3, gen_rtx_REG (HImode, X_REGNUM))"
[(set (reg:HI D_REGNUM) (match_dup 5))
(set (reg:HI D_REGNUM) (ashift:HI (reg:HI D_REGNUM) (const_int 1)))
(set (match_dup 3) (reg:HI D_REGNUM))
(set (reg:HI D_REGNUM) (match_dup 6))
(parallel [(set (reg:HI D_REGNUM)
(rotate:HI (reg:HI D_REGNUM) (const_int 1)))
(clobber (reg:HI CC_REGNUM))])
(set (match_dup 4) (reg:HI D_REGNUM))]
"operands[5] = m68hc11_gen_lowpart (HImode, operands[1]);
operands[6] = m68hc11_gen_highpart (HImode, operands[1]);")
 
;;
;; Replace a "ldd <mem>; psha; pshb" with a "ldx <mem>; pshx".
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "memory_operand" ""))
(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
(match_dup 0))
(match_scratch:HI 2 "x")]
"TARGET_M6811 && D_REG_P (operands[0]) && peep2_reg_dead_p (2, operands[0])"
[(set (match_dup 2) (match_dup 1))
(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))]
"")
 
;;
;; Remove one load when copying a value to/from memory and also
;; to a register. Take care not clobbering a possible register used
;; by operand 2.
;; Replace: "ldd 0,y; std 2,y; ldx 0,y" into "ldx 0,y; stx 2,y"
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "general_operand" ""))
(set (match_operand:HI 2 "nonimmediate_operand" "") (match_dup 0))
(set (match_operand:HI 3 "hard_reg_operand" "") (match_dup 1))]
"peep2_reg_dead_p (2, operands[0])
&& !side_effects_p (operands[1])
&& !side_effects_p (operands[2])
&& !reg_mentioned_p (operands[3], operands[2])"
[(set (match_dup 3) (match_dup 1))
(set (match_dup 2) (match_dup 3))]
"")
 
;;
;; Replace a "ldd <mem>; addd #N; std <mem>" into a
;; "ldx <mem>; leax; stx <mem>" if we have a free X/Y register
;; and the constant is small.
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "general_operand" ""))
(set (match_dup 0) (plus:HI (match_dup 0)
(match_operand:HI 2 "const_int_operand" "")))
(set (match_operand:HI 3 "nonimmediate_operand" "")
(match_dup 0))
(match_scratch:HI 4 "xy")]
"D_REG_P (operands[0])
&& (TARGET_M6812
|| (INTVAL (operands[2]) >= -2 && INTVAL (operands[2]) <= 2))
&& peep2_reg_dead_p (3, operands[0])"
[(set (match_dup 4) (match_dup 1))
(set (match_dup 4) (plus:HI (match_dup 4) (match_dup 2)))
(set (match_dup 3) (match_dup 4))]
"if (reg_mentioned_p (operands[4], operands[1])) FAIL;
if (reg_mentioned_p (operands[4], operands[3])) FAIL;")
 
;;--------------------------------------------------------------------
;;- Bset peephole2
;;--------------------------------------------------------------------
;; These peepholes try to replace some logical sequences by 'bset' and 'bclr'.
;;
;; Replace 'ldab <mem>; orab #N; stab <mem>' by 'bset <mem> #N'.
;; Register D must be dead and there must be no register side effects for mem.
;; The <mem> *can* be volatile this is why we must not use 'side_effects_p'.
;; The good side effect is that it makes the sequence atomic.
;;
(define_peephole2
[(set (match_operand:QI 0 "hard_reg_operand" "")
(match_operand:QI 1 "nonimmediate_operand" ""))
(set (match_dup 0) (ior:QI (match_dup 0)
(match_operand:QI 2 "const_int_operand" "")))
(set (match_dup 1) (match_dup 0))]
"(TARGET_M6812 || m68hc11_indirect_p (operands[1], QImode))
&& (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
&& peep2_reg_dead_p (3, operands[0])"
[(set (match_dup 1) (ior:QI (match_dup 1) (match_dup 2)))]
"")
 
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "nonimmediate_operand" ""))
(set (match_dup 0) (ior:HI (match_dup 0)
(match_operand:HI 2 "const_int_operand" "")))
(set (match_dup 1) (match_dup 0))]
"(TARGET_M6812 || m68hc11_indirect_p (operands[1], HImode))
&& (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
&& peep2_reg_dead_p (3, operands[0])"
[(set (match_dup 1) (ior:HI (match_dup 1) (match_dup 2)))]
"")
 
;;--------------------------------------------------------------------
;;- Bclr peephole2
;;--------------------------------------------------------------------
;; Replace 'ldab <mem>; andab #N; stab <mem>' by 'bclr <mem> #N'.
;; See Bset peephole2.
;;
(define_peephole2
[(set (match_operand:QI 0 "hard_reg_operand" "")
(match_operand:QI 1 "nonimmediate_operand" ""))
(set (match_dup 0) (and:QI (match_dup 0)
(match_operand:QI 2 "const_int_operand" "")))
(set (match_dup 1) (match_dup 0))]
"(TARGET_M6812 || m68hc11_indirect_p (operands[1], QImode))
&& (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
&& peep2_reg_dead_p (3, operands[0])"
[(set (match_dup 1) (and:QI (match_dup 1) (match_dup 2)))]
"")
 
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "nonimmediate_operand" ""))
(set (match_dup 0) (and:HI (match_dup 0)
(match_operand:HI 2 "const_int_operand" "")))
(set (match_dup 1) (match_dup 0))]
"(TARGET_M6812 || m68hc11_indirect_p (operands[1], HImode))
&& (GET_CODE (operands[1]) != MEM || !auto_inc_p (XEXP (operands[1], 0)))
&& peep2_reg_dead_p (3, operands[0])"
[(set (match_dup 1) (and:HI (match_dup 1) (match_dup 2)))]
"")
 
 
;;--------------------------------------------------------------------
;;- Compare peephole2
;;--------------------------------------------------------------------
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "hard_reg_operand" ""))
(set (match_dup 1) (plus:HI (match_dup 1)
(match_operand:HI 2 "const_int_operand" "")))
(set (cc0) (match_dup 0))]
"peep2_reg_dead_p (3, operands[0]) && !Z_REG_P (operands[1])"
[(set (match_dup 1) (plus:HI (match_dup 1) (match_dup 2)))
(set (cc0) (compare (match_dup 1) (match_dup 2)))]
"")
 
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "hard_reg_operand" ""))
(set (match_operand:HI 2 "hard_reg_operand" "")
(plus:HI (match_dup 2)
(match_operand:HI 3 "const_int_operand" "")))
(set (match_operand:HI 4 "memory_operand" "") (match_dup 2))
(set (cc0) (match_operand:HI 5 "hard_reg_operand" ""))]
"peep2_reg_dead_p (4, operands[5]) && !Z_REG_P (operands[2])
&& !reg_mentioned_p (operands[2], operands[4])
 
&& ((rtx_equal_p (operands[5], operands[0])
&& rtx_equal_p (operands[2], operands[1]))
 
|| (rtx_equal_p (operands[5], operands[1])
&& rtx_equal_p (operands[2], operands[0])))"
[(set (match_dup 2) (match_dup 1))
(set (match_dup 2) (plus:HI (match_dup 2) (match_dup 3)))
(set (match_dup 4) (match_dup 2))
(set (cc0) (compare (match_dup 2) (match_dup 3)))]
"")
 
 
;;--------------------------------------------------------------------
;;- Load peephole2
;;--------------------------------------------------------------------
;;
;; Optimize initialization of 2 hard regs from the same memory location
;; Since we can't copy easily X, Y and D to each other, load the 2 registers
;; from the same memory location.
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "memory_operand" ""))
(set (match_operand:HI 2 "hard_reg_operand" "") (match_dup 0))]
"TARGET_M6811
&& !side_effects_p (operands[1])
&& !reg_mentioned_p (operands[0], operands[1])"
[(set (match_dup 0) (match_dup 1))
(set (match_dup 2) (match_dup 1))]
"")
 
;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
;;
(define_peephole2
[(set (match_operand:HI 0 "nonimmediate_operand" "") (const_int 0))
(set (match_operand:HI 1 "nonimmediate_operand" "") (const_int 0))
(set (match_operand:HI 2 "nonimmediate_operand" "") (const_int 0))
(set (match_operand:HI 3 "nonimmediate_operand" "") (const_int 0))
(match_scratch:HI 4 "d")]
""
[(set (match_dup 4) (const_int 0))
(set (match_dup 0) (match_dup 4))
(set (match_dup 1) (match_dup 4))
(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 4))]
"")
 
;;
;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
;;
(define_peephole2
[(set (match_operand:HI 0 "nonimmediate_operand" "") (const_int 0))
(set (match_operand:HI 1 "nonimmediate_operand" "") (const_int 0))
(set (match_operand:HI 2 "nonimmediate_operand" "") (const_int 0))
(match_scratch:HI 3 "d")]
""
[(set (match_dup 3) (const_int 0))
(set (match_dup 0) (match_dup 3))
(set (match_dup 1) (match_dup 3))
(set (match_dup 2) (match_dup 3))]
"")
 
;;
;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
;;
(define_peephole2
[(set (match_operand:HI 0 "hard_reg_operand" "") (const_int 0))
(set (match_operand:HI 1 "push_operand" "") (match_dup 0))
(set (match_operand:HI 2 "push_operand" "") (match_dup 0))
(set (match_operand:HI 3 "push_operand" "") (match_dup 0))
(match_scratch:HI 4 "x")]
"TARGET_M6811 && D_REG_P (operands[0]) && peep2_reg_dead_p (4, operands[0])"
[(set (match_dup 4) (const_int 0))
(set (match_dup 1) (match_dup 4))
(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 4))]
"")
 
;;
;; This peephole catches the address computations generated by the reload
;; pass.
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "xy")
(match_operand:HI 1 "const_int_operand" ""))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM)
(plus (reg:HI D_REGNUM)
(match_operand:HI 2 "general_operand" "")))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"(INTVAL (operands[1]) & 0x0FF) == 0"
"*
{
int value_loaded = 1;
 
if (X_REG_P (operands[0]) || SP_REG_P (operands[2]))
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = operands[2];
m68hc11_gen_movhi (insn, ops);
output_asm_insn (\"xgd%0\", operands);
}
else if (Y_REG_P (operands[0]))
{
if (reg_mentioned_p (iy_reg, operands[2]))
output_asm_insn (\"ldy\\t%2\", operands);
else
value_loaded = 0;
output_asm_insn (\"xgdy\", operands);
}
else
{
output_asm_insn (\"ldd\\t%2\", operands);
}
 
if (value_loaded == 0)
output_asm_insn (\"ldd\\t%2\", operands);
if ((INTVAL (operands[1]) & 0x0ff00) == 0x100)
output_asm_insn (\"inca\", operands);
else if ((INTVAL (operands[1]) & 0x0ff00) == 0xff00)
output_asm_insn (\"deca\", operands);
else if (INTVAL (operands[1]) != 0)
output_asm_insn (\"adda\\t%h1\", operands);
 
if (X_REG_P (operands[0]))
return \"xgdx\";
else if (Y_REG_P (operands[0]))
return \"xgdy\";
else
return \"\";
}
")
 
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "h")
(match_operand:HI 1 "non_push_operand" "g"))
(set (match_operand:HI 2 "hard_reg_operand" "h")
(match_dup 0))]
"find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
&& !S_REG_P (operands[2])"
"*
{
rtx ops[2];
 
ops[0] = operands[2];
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "h")
(match_operand:HI 1 "hard_reg_operand" "h"))
(set (match_operand:HI 2 "non_push_operand" "g")
(match_dup 0))]
"find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
&& !S_REG_P (operands[2])"
"*
{
rtx ops[2];
 
ops[0] = operands[2];
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
;;
;; Catch a (set X/Y D) followed by a swap. In this form, D is dead after
;; the set, so we don't need to emit anything. 'ins1' refers to the
;; (set ...) insn.
;;
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
"*
{
cc_status = cc_prev_status;
return \"\";
}
")
 
;; Same as above but due to some split, there may be a noop set
;; between the two.
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
(set (match_dup 0) (match_dup 0))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
"find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
"*
{
cc_status = cc_prev_status;
return \"\";
}
")
 
;;
;; Catch a (set X/Y D) followed by an xgdx/xgdy. D is not dead
;; and we must, at least, setup X/Y with value of D.
;;
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
""
"*
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
;;;
(define_peephole
[(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM) (match_dup 0))]
"find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
"*
{
cc_status = cc_prev_status;
return \"\";
}
")
 
;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
;;;
(define_peephole
[(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
"REGNO (operands[0]) == REGNO (operands[1])
&& find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
"*
{
cc_status = cc_prev_status;
return \"\";
}
")
 
;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
;;;
(define_peephole
[(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:HI D_REGNUM) (match_dup 0))]
""
"*
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
;;;
;;; Same peephole with a QI set. The copy is made as 16-bit to comply
;;; with the xgdx.
;;;
(define_peephole
[(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
(set (match_dup 0) (reg:HI D_REGNUM))])
(set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
"REGNO (operands[0]) == REGNO (operands[1])"
"*
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
;;;
;;; Catch two consecutive xgdx or xgdy, emit nothing.
;;;
(define_peephole
[(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
(set (match_dup 0) (reg:HI D_REGNUM))])
(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
(set (match_dup 0) (reg:HI D_REGNUM))])]
""
"*
{
cc_status = cc_prev_status;
return \"\";
}
")
 
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "stack_register_operand" ""))
(set (match_operand:HI 2 "hard_reg_operand" "")
(match_operand:HI 3 "memory_operand" "m"))
(set (match_dup 0)
(match_operand:HI 4 "memory_operand" "m"))]
"IS_STACK_POP (operands[4])
&& (GET_CODE (operands[3]) == MEM &&
rtx_equal_p (operands[0], XEXP (operands[3], 0)))"
"*
{
rtx ops[2];
 
ops[0] = operands[2];
ops[1] = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode, stack_pointer_rtx));
m68hc11_gen_movhi (insn, ops);
return \"\";
}
")
 
;;
;; Catch (d = -1) (d = d + sp) to avoid 2 adjust of SP.
;;
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "dA") (const_int -1))
(set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
"TARGET_M6811"
"*
{
return \"sts\\t%t0\\n\\tld%0\\t%t0\";
}
")
 
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "")
(match_operand:HI 1 "memory_operand" ""))
(set (match_operand:HI 2 "hard_reg_operand" "") (match_dup 0))]
"TARGET_M6811
&& !side_effects_p (operands[1])
&& !reg_mentioned_p (operands[0], operands[1])"
"*
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
ops[0] = operands[2];
m68hc11_gen_movhi (insn, ops);
return \"\";
}")
 
;; Peephole for Z register replacement.
;; Avoid to use _.tmp register when comparing D and X if we can compare
;; with soft register
(define_peephole
[(set (match_operand:HI 0 "hard_reg_operand" "") (reg:HI SOFT_XY_REGNUM))
(set (reg:HI SOFT_TMP_REGNUM) (match_dup 0))
(set (cc0) (compare (match_operand:HI 2 "hard_reg_operand" "")
(reg:HI SOFT_TMP_REGNUM)))]
"X_REG_P (operands[0]) || Y_REG_P (operands[0])"
"*
{
rtx ops[2];
 
ops[0] = operands[0];
ops[1] = operands[1];
m68hc11_gen_movhi (insn, ops);
return \"cp%2\\t%1\";
}")
/m68hc11.c
0,0 → 1,5522
/* Subroutines for code generation on Motorola 68HC11 and 68HC12.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
Contributed by Stephane Carrez (stcarrez@nerim.fr)
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>.
 
Note:
A first 68HC11 port was made by Otto Lind (otto@coactive.com)
on gcc 2.6.3. I have used it as a starting point for this port.
However, this new port is a complete re-write. Its internal
design is completely different. The generated code is not
compatible with the gcc 2.6.3 port.
 
The gcc 2.6.3 port is available at:
 
ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
 
*/
 
#include <stdio.h>
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
#include "libfuncs.h"
#include "toplev.h"
#include "basic-block.h"
#include "function.h"
#include "ggc.h"
#include "reload.h"
#include "target.h"
#include "target-def.h"
 
static void emit_move_after_reload (rtx, rtx, rtx);
static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
static void m68hc11_emit_logical (enum machine_mode, int, rtx *);
static void m68hc11_reorg (void);
static int go_if_legitimate_address_internal (rtx, enum machine_mode, int);
static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
static int must_parenthesize (rtx);
static int m68hc11_address_cost (rtx);
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 *);
static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
const struct attribute_spec m68hc11_attribute_table[];
 
void create_regs_rtx (void);
 
static void asm_print_register (FILE *, int);
static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void m68hc11_asm_out_constructor (rtx, int);
static void m68hc11_asm_out_destructor (rtx, int);
static void m68hc11_file_start (void);
static void m68hc11_encode_section_info (tree, rtx, int);
static const char *m68hc11_strip_name_encoding (const char* str);
static unsigned int m68hc11_section_type_flags (tree, const char*, int);
static int autoinc_mode (rtx);
static int m68hc11_make_autoinc_notes (rtx *, void *);
static void m68hc11_init_libfuncs (void);
static rtx m68hc11_struct_value_rtx (tree, int);
static bool m68hc11_return_in_memory (tree, tree);
 
/* Must be set to 1 to produce debug messages. */
int debug_m6811 = 0;
 
extern FILE *asm_out_file;
 
rtx ix_reg;
rtx iy_reg;
rtx d_reg;
rtx m68hc11_soft_tmp_reg;
static GTY(()) rtx stack_push_word;
static GTY(()) rtx stack_pop_word;
static GTY(()) rtx z_reg;
static GTY(()) rtx z_reg_qi;
static int regs_inited = 0;
 
/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
int current_function_interrupt;
 
/* Set to 1 by expand_prologue() when the function is a trap handler. */
int current_function_trap;
 
/* Set to 1 when the current function is placed in 68HC12 banked
memory and must return with rtc. */
int current_function_far;
 
/* Min offset that is valid for the indirect addressing mode. */
HOST_WIDE_INT m68hc11_min_offset = 0;
 
/* Max offset that is valid for the indirect addressing mode. */
HOST_WIDE_INT m68hc11_max_offset = 256;
 
/* The class value for base registers. */
enum reg_class m68hc11_base_reg_class = A_REGS;
 
/* The class value for index registers. This is NO_REGS for 68HC11. */
enum reg_class m68hc11_index_reg_class = NO_REGS;
 
enum reg_class m68hc11_tmp_regs_class = NO_REGS;
 
/* Tables that tell whether a given hard register is valid for
a base or an index register. It is filled at init time depending
on the target processor. */
unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
 
/* A correction offset which is applied to the stack pointer.
This is 1 for 68HC11 and 0 for 68HC12. */
int m68hc11_sp_correction;
 
int m68hc11_addr_mode;
int m68hc11_mov_addr_mode;
 
/* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
rtx m68hc11_compare_op0;
rtx m68hc11_compare_op1;
 
const struct processor_costs *m68hc11_cost;
 
/* Costs for a 68HC11. */
static const struct processor_costs m6811_cost = {
/* add */
COSTS_N_INSNS (2),
/* logical */
COSTS_N_INSNS (2),
/* non-constant shift */
COSTS_N_INSNS (20),
/* shiftQI const */
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
 
/* shiftHI const */
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
COSTS_N_INSNS (4), COSTS_N_INSNS (2),
COSTS_N_INSNS (2), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
},
/* mulQI */
COSTS_N_INSNS (20),
/* mulHI */
COSTS_N_INSNS (20 * 4),
/* mulSI */
COSTS_N_INSNS (20 * 16),
/* divQI */
COSTS_N_INSNS (20),
/* divHI */
COSTS_N_INSNS (80),
/* divSI */
COSTS_N_INSNS (100)
};
 
/* Costs for a 68HC12. */
static const struct processor_costs m6812_cost = {
/* add */
COSTS_N_INSNS (2),
/* logical */
COSTS_N_INSNS (2),
/* non-constant shift */
COSTS_N_INSNS (20),
/* shiftQI const */
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
 
/* shiftHI const */
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
COSTS_N_INSNS (4), COSTS_N_INSNS (2),
COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
COSTS_N_INSNS (6), COSTS_N_INSNS (4)
},
/* mulQI */
COSTS_N_INSNS (3),
/* mulHI */
COSTS_N_INSNS (3),
/* mulSI */
COSTS_N_INSNS (3 * 4),
/* divQI */
COSTS_N_INSNS (12),
/* divHI */
COSTS_N_INSNS (12),
/* divSI */
COSTS_N_INSNS (100)
};
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
 
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
 
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
 
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START m68hc11_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
 
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
 
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS m68hc11_section_type_flags
 
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS m68hc11_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST m68hc11_address_cost
 
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
 
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS m68hc11_init_libfuncs
 
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX m68hc11_struct_value_rtx
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY m68hc11_return_in_memory
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_callee_copies_named
 
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
 
struct gcc_target targetm = TARGET_INITIALIZER;
int
m68hc11_override_options (void)
{
memset (m68hc11_reg_valid_for_index, 0,
sizeof (m68hc11_reg_valid_for_index));
memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
 
/* Compilation with -fpic generates a wrong code. */
if (flag_pic)
{
warning (0, "-f%s ignored for 68HC11/68HC12 (not supported)",
(flag_pic > 1) ? "PIC" : "pic");
flag_pic = 0;
}
 
/* Do not enable -fweb because it breaks the 32-bit shift patterns
by breaking the match_dup of those patterns. The shift patterns
will no longer be recognized after that. */
flag_web = 0;
 
/* Configure for a 68hc11 processor. */
if (TARGET_M6811)
{
target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
m68hc11_cost = &m6811_cost;
m68hc11_min_offset = 0;
m68hc11_max_offset = 256;
m68hc11_index_reg_class = NO_REGS;
m68hc11_base_reg_class = A_REGS;
m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
m68hc11_sp_correction = 1;
m68hc11_tmp_regs_class = D_REGS;
m68hc11_addr_mode = ADDR_OFFSET;
m68hc11_mov_addr_mode = 0;
if (m68hc11_soft_reg_count < 0)
m68hc11_soft_reg_count = 4;
}
 
/* Configure for a 68hc12 processor. */
if (TARGET_M6812)
{
m68hc11_cost = &m6812_cost;
m68hc11_min_offset = -65536;
m68hc11_max_offset = 65536;
m68hc11_index_reg_class = D_REGS;
m68hc11_base_reg_class = A_OR_SP_REGS;
m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
m68hc11_sp_correction = 0;
m68hc11_tmp_regs_class = TMP_REGS;
m68hc11_addr_mode = ADDR_INDIRECT | ADDR_OFFSET | ADDR_CONST
| (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
m68hc11_mov_addr_mode = ADDR_OFFSET | ADDR_CONST
| (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
target_flags |= MASK_NO_DIRECT_MODE;
if (m68hc11_soft_reg_count < 0)
m68hc11_soft_reg_count = 0;
 
if (TARGET_LONG_CALLS)
current_function_far = 1;
}
return 0;
}
 
 
void
m68hc11_conditional_register_usage (void)
{
int i;
 
if (m68hc11_soft_reg_count > SOFT_REG_LAST - SOFT_REG_FIRST)
m68hc11_soft_reg_count = SOFT_REG_LAST - SOFT_REG_FIRST;
 
for (i = SOFT_REG_FIRST + m68hc11_soft_reg_count; i < SOFT_REG_LAST; i++)
{
fixed_regs[i] = 1;
call_used_regs[i] = 1;
}
 
/* For 68HC12, the Z register emulation is not necessary when the
frame pointer is not used. The frame pointer is eliminated and
replaced by the stack register (which is a BASE_REG_CLASS). */
if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
{
fixed_regs[HARD_Z_REGNUM] = 1;
}
}
 
/* Reload and register operations. */
 
 
void
create_regs_rtx (void)
{
/* regs_inited = 1; */
ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
d_reg = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_soft_tmp_reg = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
 
stack_push_word = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
stack_pop_word = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
 
}
 
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
- 8 bit values are stored anywhere (except the SP register).
- 16 bit values can be stored in any register whose mode is 16
- 32 bit values can be stored in D, X registers or in a soft register
(except the last one because we need 2 soft registers)
- Values whose size is > 32 bit are not stored in real hard
registers. They may be stored in soft registers if there are
enough of them. */
int
hard_regno_mode_ok (int regno, enum machine_mode mode)
{
switch (GET_MODE_SIZE (mode))
{
case 8:
return S_REGNO_P (regno) && m68hc11_soft_reg_count >= 4;
 
case 4:
return (X_REGNO_P (regno)
|| (S_REGNO_P (regno) && m68hc11_soft_reg_count >= 2));
 
case 2:
return G_REGNO_P (regno);
 
case 1:
/* We have to accept a QImode in X or Y registers. Otherwise, the
reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
in the insns. Reload fails if the insn rejects the register class 'a'
as well as if it accepts it. Patterns that failed were
zero_extend_qihi2 and iorqi3. */
 
return G_REGNO_P (regno) && !SP_REGNO_P (regno);
 
default:
return 0;
}
}
 
int
m68hc11_hard_regno_rename_ok (int reg1, int reg2)
{
/* Don't accept renaming to Z register. We will replace it to
X,Y or D during machine reorg pass. */
if (reg2 == HARD_Z_REGNUM)
return 0;
 
/* Don't accept renaming D,X to Y register as the code will be bigger. */
if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
&& (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
return 0;
 
return 1;
}
 
enum reg_class
preferred_reload_class (rtx operand, enum reg_class class)
{
enum machine_mode mode;
 
mode = GET_MODE (operand);
 
if (debug_m6811)
{
printf ("Preferred reload: (class=%s): ", reg_class_names[class]);
}
 
if (class == D_OR_A_OR_S_REGS && SP_REG_P (operand))
return m68hc11_base_reg_class;
 
if (class >= 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 (class)
{
default:
case G_REGS:
case D_OR_A_OR_S_REGS:
class = A_OR_D_REGS;
break;
case A_OR_S_REGS:
class = A_REGS;
break;
case D_OR_SP_OR_S_REGS:
class = D_OR_SP_REGS;
break;
case D_OR_Y_OR_S_REGS:
class = D_OR_Y_REGS;
break;
case D_OR_X_OR_S_REGS:
class = D_OR_X_REGS;
break;
case SP_OR_S_REGS:
class = SP_REGS;
break;
case Y_OR_S_REGS:
class = Y_REGS;
break;
case X_OR_S_REGS:
class = X_REGS;
break;
case D_OR_S_REGS:
class = D_REGS;
}
}
else if (class == Y_REGS && GET_CODE (operand) == MEM)
{
class = Y_REGS;
}
else if (class == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
{
class = D_OR_X_REGS;
}
else if (class >= S_REGS && S_REG_P (operand))
{
switch (class)
{
default:
case G_REGS:
case D_OR_A_OR_S_REGS:
class = A_OR_D_REGS;
break;
case A_OR_S_REGS:
class = A_REGS;
break;
case D_OR_SP_OR_S_REGS:
class = D_OR_SP_REGS;
break;
case D_OR_Y_OR_S_REGS:
class = D_OR_Y_REGS;
break;
case D_OR_X_OR_S_REGS:
class = D_OR_X_REGS;
break;
case SP_OR_S_REGS:
class = SP_REGS;
break;
case Y_OR_S_REGS:
class = Y_REGS;
break;
case X_OR_S_REGS:
class = X_REGS;
break;
case D_OR_S_REGS:
class = D_REGS;
}
}
else if (class >= S_REGS)
{
if (debug_m6811)
{
printf ("Class = %s for: ", reg_class_names[class]);
fflush (stdout);
debug_rtx (operand);
}
}
 
if (debug_m6811)
{
printf (" => class=%s\n", reg_class_names[class]);
fflush (stdout);
debug_rtx (operand);
}
 
return class;
}
 
/* Return 1 if the operand is a valid indexed addressing mode.
For 68hc11: n,r with n in [0..255] and r in A_REGS class
For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
int
m68hc11_valid_addressing_p (rtx operand, enum machine_mode mode, int addr_mode)
{
rtx base, offset;
 
switch (GET_CODE (operand))
{
case MEM:
if ((addr_mode & ADDR_INDIRECT) && GET_MODE_SIZE (mode) <= 2)
return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
addr_mode & (ADDR_STRICT | ADDR_OFFSET));
return 0;
 
case POST_INC:
case PRE_INC:
case POST_DEC:
case PRE_DEC:
if (addr_mode & ADDR_INCDEC)
return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
addr_mode & ADDR_STRICT);
return 0;
 
case PLUS:
base = XEXP (operand, 0);
if (GET_CODE (base) == MEM)
return 0;
 
offset = XEXP (operand, 1);
if (GET_CODE (offset) == MEM)
return 0;
 
/* Indexed addressing mode with 2 registers. */
if (GET_CODE (base) == REG && GET_CODE (offset) == REG)
{
if (!(addr_mode & ADDR_INDEXED))
return 0;
 
addr_mode &= ADDR_STRICT;
if (REGNO_OK_FOR_BASE_P2 (REGNO (base), addr_mode)
&& REGNO_OK_FOR_INDEX_P2 (REGNO (offset), addr_mode))
return 1;
 
if (REGNO_OK_FOR_BASE_P2 (REGNO (offset), addr_mode)
&& REGNO_OK_FOR_INDEX_P2 (REGNO (base), addr_mode))
return 1;
 
return 0;
}
 
if (!(addr_mode & ADDR_OFFSET))
return 0;
 
if (GET_CODE (base) == REG)
{
if (!VALID_CONSTANT_OFFSET_P (offset, mode))
return 0;
 
if (!(addr_mode & ADDR_STRICT))
return 1;
 
return REGNO_OK_FOR_BASE_P2 (REGNO (base), 1);
}
 
if (GET_CODE (offset) == REG)
{
if (!VALID_CONSTANT_OFFSET_P (base, mode))
return 0;
 
if (!(addr_mode & ADDR_STRICT))
return 1;
 
return REGNO_OK_FOR_BASE_P2 (REGNO (offset), 1);
}
return 0;
 
case REG:
return REGNO_OK_FOR_BASE_P2 (REGNO (operand), addr_mode & ADDR_STRICT);
 
case CONST_INT:
if (addr_mode & ADDR_CONST)
return VALID_CONSTANT_OFFSET_P (operand, mode);
return 0;
 
default:
return 0;
}
}
 
/* Returns 1 if the operand fits in a 68HC11 indirect mode or in
a 68HC12 1-byte index addressing mode. */
int
m68hc11_small_indexed_indirect_p (rtx operand, enum machine_mode mode)
{
rtx base, offset;
int addr_mode;
 
if (GET_CODE (operand) == REG && reload_in_progress
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
operand = eliminate_regs (operand, 0, NULL_RTX);
}
 
if (GET_CODE (operand) != MEM)
return 0;
 
operand = XEXP (operand, 0);
if (CONSTANT_ADDRESS_P (operand))
return 1;
 
if (PUSH_POP_ADDRESS_P (operand))
return 1;
 
addr_mode = m68hc11_mov_addr_mode | (reload_completed ? ADDR_STRICT : 0);
if (!m68hc11_valid_addressing_p (operand, mode, addr_mode))
return 0;
 
if (TARGET_M6812 && GET_CODE (operand) == PLUS
&& (reload_completed | reload_in_progress))
{
base = XEXP (operand, 0);
offset = XEXP (operand, 1);
 
/* The offset can be a symbol address and this is too big
for the operand constraint. */
if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
return 0;
 
if (GET_CODE (base) == CONST_INT)
offset = base;
 
switch (GET_MODE_SIZE (mode))
{
case 8:
if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
return 0;
break;
 
case 4:
if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
return 0;
break;
 
default:
if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
return 0;
break;
}
}
return 1;
}
 
int
m68hc11_register_indirect_p (rtx operand, enum machine_mode mode)
{
int addr_mode;
 
if (GET_CODE (operand) == REG && reload_in_progress
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
operand = eliminate_regs (operand, 0, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
return 0;
 
operand = XEXP (operand, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
return m68hc11_valid_addressing_p (operand, mode, addr_mode);
}
 
static int
go_if_legitimate_address_internal (rtx operand, enum machine_mode mode,
int strict)
{
int addr_mode;
 
if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
{
/* Reject the global variables if they are too wide. This forces
a load of their address in a register and generates smaller code. */
if (GET_MODE_SIZE (mode) == 8)
return 0;
 
return 1;
}
addr_mode = m68hc11_addr_mode | (strict ? ADDR_STRICT : 0);
if (m68hc11_valid_addressing_p (operand, mode, addr_mode))
{
return 1;
}
if (PUSH_POP_ADDRESS_P (operand))
{
return 1;
}
if (symbolic_memory_operand (operand, mode))
{
return 1;
}
return 0;
}
 
int
m68hc11_go_if_legitimate_address (rtx operand, enum machine_mode mode,
int strict)
{
int result;
 
if (debug_m6811)
{
printf ("Checking: ");
fflush (stdout);
debug_rtx (operand);
}
 
result = go_if_legitimate_address_internal (operand, mode, strict);
 
if (debug_m6811)
{
printf (" -> %s\n", result == 0 ? "NO" : "YES");
}
 
if (result == 0)
{
if (debug_m6811)
{
printf ("go_if_legitimate%s, ret 0: %d:",
(strict ? "_strict" : ""), mode);
fflush (stdout);
debug_rtx (operand);
}
}
return result;
}
 
int
m68hc11_legitimize_address (rtx *operand ATTRIBUTE_UNUSED,
rtx old_operand ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
return 0;
}
 
 
int
m68hc11_reload_operands (rtx operands[])
{
enum machine_mode mode;
 
if (regs_inited == 0)
create_regs_rtx ();
 
mode = GET_MODE (operands[1]);
 
/* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
{
rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
rtx base = XEXP (XEXP (operands[1], 0), 0);
 
if (GET_CODE (base) != REG)
{
rtx tmp = base;
base = big_offset;
big_offset = tmp;
}
 
/* If the offset is out of range, we have to compute the address
with a separate add instruction. We try to do this with an 8-bit
add on the A register. This is possible only if the lowest part
of the offset (i.e., big_offset % 256) is a valid constant offset
with respect to the mode. If it's not, we have to generate a
16-bit add on the D register. From:
(SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
we generate:
[(SET (REG D) (REG X)) (SET (REG X) (REG D))]
(SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
[(SET (REG D) (REG X)) (SET (REG X) (REG D))]
(SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
(SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
(SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
 
*/
if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
{
int vh, vl;
rtx reg = operands[0];
rtx offset;
int val = INTVAL (big_offset);
 
 
/* We use the 'operands[0]' as a scratch register to compute the
address. Make sure 'base' is in that register. */
if (!rtx_equal_p (base, operands[0]))
{
emit_move_insn (reg, base);
}
 
if (val > 0)
{
vh = val >> 8;
vl = val & 0x0FF;
}
else
{
vh = (val >> 8) & 0x0FF;
vl = val & 0x0FF;
}
 
/* Create the lowest part offset that still remains to be added.
If it's not a valid offset, do a 16-bit add. */
offset = GEN_INT (vl);
if (!VALID_CONSTANT_OFFSET_P (offset, mode))
{
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (HImode, reg, big_offset)));
offset = const0_rtx;
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (HImode, reg,
GEN_INT (vh << 8))));
}
emit_move_insn (operands[0],
gen_rtx_MEM (GET_MODE (operands[1]),
gen_rtx_PLUS (Pmode, reg, offset)));
return 1;
}
}
 
/* Use the normal gen_movhi pattern. */
return 0;
}
 
void
m68hc11_emit_libcall (const char *name, enum rtx_code code,
enum machine_mode dmode, enum machine_mode smode,
int noperands, rtx *operands)
{
rtx ret;
rtx insns;
rtx libcall;
rtx equiv;
 
start_sequence ();
libcall = gen_rtx_SYMBOL_REF (Pmode, name);
switch (noperands)
{
case 2:
ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
dmode, 1, operands[1], smode);
equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
break;
 
case 3:
ret = emit_library_call_value (libcall, NULL_RTX,
LCT_CONST, dmode, 2,
operands[1], smode, operands[2],
smode);
equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
break;
 
default:
gcc_unreachable ();
}
 
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, operands[0], ret, equiv);
}
 
/* Returns true if X is a PRE/POST increment decrement
(same as auto_inc_p() in rtlanal.c but do not take into
account the stack). */
int
m68hc11_auto_inc_p (rtx x)
{
return GET_CODE (x) == PRE_DEC
|| GET_CODE (x) == POST_INC
|| GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
}
 
/* Predicates for machine description. */
 
int
memory_reload_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (operand) == MEM
&& GET_CODE (XEXP (operand, 0)) == PLUS
&& ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
&& GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
|| (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
&& GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
}
 
int
m68hc11_symbolic_p (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM)
{
rtx op = XEXP (operand, 0);
 
if (symbolic_memory_operand (op, mode))
return 1;
}
return 0;
}
 
int
m68hc11_indirect_p (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM && GET_MODE (operand) == mode)
{
rtx op = XEXP (operand, 0);
int addr_mode;
 
if (m68hc11_page0_symbol_p (op))
return 1;
 
if (symbolic_memory_operand (op, mode))
return TARGET_M6812;
 
if (reload_in_progress)
return 1;
 
operand = XEXP (operand, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
return m68hc11_valid_addressing_p (operand, mode, addr_mode);
}
return 0;
}
 
int
memory_indexed_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (operand) != MEM)
return 0;
 
operand = XEXP (operand, 0);
if (GET_CODE (operand) == PLUS)
{
if (GET_CODE (XEXP (operand, 0)) == REG)
operand = XEXP (operand, 0);
else if (GET_CODE (XEXP (operand, 1)) == REG)
operand = XEXP (operand, 1);
}
return GET_CODE (operand) == REG
&& (REGNO (operand) >= FIRST_PSEUDO_REGISTER
|| A_REGNO_P (REGNO (operand)));
}
 
int
push_pop_operand_p (rtx operand)
{
if (GET_CODE (operand) != MEM)
{
return 0;
}
operand = XEXP (operand, 0);
return PUSH_POP_ADDRESS_P (operand);
}
 
/* Returns 1 if OP is either a symbol reference or a sum of a symbol
reference and a constant. */
 
int
symbolic_memory_operand (rtx op, enum machine_mode mode)
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
 
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
 
/* ??? This clause seems to be irrelevant. */
case CONST_DOUBLE:
return GET_MODE (op) == mode;
 
case PLUS:
return symbolic_memory_operand (XEXP (op, 0), mode)
&& symbolic_memory_operand (XEXP (op, 1), mode);
 
default:
return 0;
}
}
/* Emit the code to build the trampoline used to call a nested function.
68HC11 68HC12
 
ldy #&CXT movw #&CXT,*_.d1
sty *_.d1 jmp FNADDR
jmp FNADDR
 
*/
void
m68hc11_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
{
const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
 
/* Skip the '*'. */
if (*static_chain_reg == '*')
static_chain_reg++;
if (TARGET_M6811)
{
emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
GEN_INT (0x18df));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
gen_rtx_CONST (QImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
GEN_INT (0x7e));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
}
else
{
emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
gen_rtx_CONST (HImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
GEN_INT (0x06));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
}
}
/* Declaration of types. */
 
/* Handle an "tiny_data" attribute; arguments as in
struct attribute_spec.handler. */
static tree
m68hc11_handle_page0_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
tree decl = *node;
 
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
DECL_SECTION_NAME (decl) = build_string (6, ".page0");
}
else
{
warning (OPT_Wattributes, "%qs attribute ignored",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
 
const struct attribute_spec m68hc11_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
{ "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
 
/* Keep track of the symbol which has a `trap' attribute and which uses
the `swi' calling convention. Since there is only one trap, we only
record one such symbol. If there are several, a warning is reported. */
static rtx trap_handler_symbol = 0;
 
/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
arguments as in struct attribute_spec.handler. */
static tree
m68hc11_handle_fntype_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE
&& TREE_CODE (*node) != METHOD_TYPE
&& TREE_CODE (*node) != FIELD_DECL
&& TREE_CODE (*node) != TYPE_DECL)
{
warning (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
/* Undo the effects of the above. */
 
static const char *
m68hc11_strip_name_encoding (const char *str)
{
return str + (*str == '*' || *str == '@' || *str == '&');
}
 
static void
m68hc11_encode_label (tree decl)
{
const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
int len = strlen (str);
char *newstr = alloca (len + 2);
 
newstr[0] = '@';
strcpy (&newstr[1], str);
 
XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 1);
}
 
/* Return 1 if this is a symbol in page0 */
int
m68hc11_page0_symbol_p (rtx x)
{
switch (GET_CODE (x))
{
case SYMBOL_REF:
return XSTR (x, 0) != 0 && XSTR (x, 0)[0] == '@';
 
case CONST:
return m68hc11_page0_symbol_p (XEXP (x, 0));
 
case PLUS:
if (!m68hc11_page0_symbol_p (XEXP (x, 0)))
return 0;
 
return GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < 256
&& INTVAL (XEXP (x, 1)) >= 0;
 
default:
return 0;
}
}
 
/* We want to recognize trap handlers so that we handle calls to traps
in a special manner (by issuing the trap). This information is stored
in SYMBOL_REF_FLAG. */
 
static void
m68hc11_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
tree func_attr;
int trap_handler;
int is_far = 0;
if (TREE_CODE (decl) == VAR_DECL)
{
if (lookup_attribute ("page0", DECL_ATTRIBUTES (decl)) != 0)
m68hc11_encode_label (decl);
return;
}
 
if (TREE_CODE (decl) != FUNCTION_DECL)
return;
 
func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
 
 
if (lookup_attribute ("far", func_attr) != NULL_TREE)
is_far = 1;
else if (lookup_attribute ("near", func_attr) == NULL_TREE)
is_far = TARGET_LONG_CALLS != 0;
 
trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
if (trap_handler && is_far)
{
warning (OPT_Wattributes, "%<trap%> and %<far%> attributes are "
"not compatible, ignoring %<far%>");
trap_handler = 0;
}
if (trap_handler)
{
if (trap_handler_symbol != 0)
warning (OPT_Wattributes, "%<trap%> attribute is already used");
else
trap_handler_symbol = XEXP (rtl, 0);
}
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
}
 
static unsigned int
m68hc11_section_type_flags (tree decl, const char *name, int reloc)
{
unsigned int flags = default_section_type_flags (decl, name, reloc);
 
if (strncmp (name, ".eeprom", 7) == 0)
{
flags |= SECTION_WRITE | SECTION_CODE | SECTION_OVERRIDE;
}
 
return flags;
}
 
int
m68hc11_is_far_symbol (rtx sym)
{
if (GET_CODE (sym) == MEM)
sym = XEXP (sym, 0);
 
return SYMBOL_REF_FLAG (sym);
}
 
int
m68hc11_is_trap_symbol (rtx sym)
{
if (GET_CODE (sym) == MEM)
sym = XEXP (sym, 0);
 
return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
}
 
/* Argument support functions. */
 
/* Define the offset between two registers, one to be eliminated, and the
other its replacement, at the start of a routine. */
int
m68hc11_initial_elimination_offset (int from, int to)
{
int trap_handler;
tree func_attr;
int size;
int regno;
 
/* For a trap handler, we must take into account the registers which
are pushed on the stack during the trap (except the PC). */
func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
current_function_interrupt = lookup_attribute ("interrupt",
func_attr) != NULL_TREE;
trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
 
if (lookup_attribute ("far", func_attr) != 0)
current_function_far = 1;
else if (lookup_attribute ("near", func_attr) != 0)
current_function_far = 0;
else
current_function_far = (TARGET_LONG_CALLS != 0
&& !current_function_interrupt
&& !trap_handler);
 
if (trap_handler && from == ARG_POINTER_REGNUM)
size = 7;
 
/* For a function using 'call/rtc' we must take into account the
page register which is pushed in the call. */
else if (current_function_far && from == ARG_POINTER_REGNUM)
size = 1;
else
size = 0;
 
if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
{
/* 2 is for the saved frame.
1 is for the 'sts' correction when creating the frame. */
return get_frame_size () + 2 + m68hc11_sp_correction + size;
}
 
if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
{
return m68hc11_sp_correction;
}
 
/* Push any 2 byte pseudo hard registers that we need to save. */
for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
size += 2;
}
}
 
if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
{
return get_frame_size () + size;
}
 
if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
{
return size;
}
return 0;
}
 
/* Initialize a variable CUM of type CUMULATIVE_ARGS
for a call to a function whose data type is FNTYPE.
For a library call, FNTYPE is 0. */
 
void
m68hc11_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
{
tree ret_type;
 
z_replacement_completed = 0;
cum->words = 0;
cum->nregs = 0;
 
/* For a library call, we must find out the type of the return value.
When the return value is bigger than 4 bytes, it is returned in
memory. In that case, the first argument of the library call is a
pointer to the memory location. Because the first argument is passed in
register D, we have to identify this, so that the first function
parameter is not passed in D either. */
if (fntype == 0)
{
const char *name;
size_t len;
 
if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
return;
 
/* If the library ends in 'di' or in 'df', we assume it's
returning some DImode or some DFmode which are 64-bit wide. */
name = XSTR (libname, 0);
len = strlen (name);
if (len > 3
&& ((name[len - 2] == 'd'
&& (name[len - 1] == 'f' || name[len - 1] == 'i'))
|| (name[len - 3] == 'd'
&& (name[len - 2] == 'i' || name[len - 2] == 'f'))))
{
/* We are in. Mark the first parameter register as already used. */
cum->words = 1;
cum->nregs = 1;
}
return;
}
 
ret_type = TREE_TYPE (fntype);
 
if (ret_type && aggregate_value_p (ret_type, fntype))
{
cum->words = 1;
cum->nregs = 1;
}
}
 
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
(TYPE is null for libcalls where that information may not be available.) */
 
void
m68hc11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named ATTRIBUTE_UNUSED)
{
if (mode != BLKmode)
{
if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
{
cum->nregs = 2;
cum->words = GET_MODE_SIZE (mode);
}
else
{
cum->words += GET_MODE_SIZE (mode);
if (cum->words <= HARD_REG_SIZE)
cum->nregs = 1;
}
}
else
{
cum->words += int_size_in_bytes (type);
}
return;
}
 
/* Define where to put the arguments to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
 
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis). */
 
struct rtx_def *
m68hc11_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type ATTRIBUTE_UNUSED, int named ATTRIBUTE_UNUSED)
{
if (cum->words != 0)
{
return NULL_RTX;
}
 
if (mode != BLKmode)
{
if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
return gen_rtx_REG (mode, HARD_X_REGNUM);
 
if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
{
return NULL_RTX;
}
return gen_rtx_REG (mode, HARD_D_REGNUM);
}
return NULL_RTX;
}
 
/* If defined, a C expression which determines whether, and in which direction,
to pad out an argument with extra space. The value should be of type
`enum direction': either `upward' to pad above the argument,
`downward' to pad below, or `none' to inhibit padding.
 
Structures are stored left shifted in their argument slot. */
int
m68hc11_function_arg_padding (enum machine_mode mode, tree type)
{
if (type != 0 && AGGREGATE_TYPE_P (type))
return upward;
 
/* Fall back to the default. */
return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
}
 
/* Function prologue and epilogue. */
 
/* Emit a move after the reload pass has completed. This is used to
emit the prologue and epilogue. */
static void
emit_move_after_reload (rtx to, rtx from, rtx scratch)
{
rtx insn;
 
if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
{
insn = emit_move_insn (to, from);
}
else
{
emit_move_insn (scratch, from);
insn = emit_move_insn (to, scratch);
}
 
/* Put a REG_INC note to tell the flow analysis that the instruction
is necessary. */
if (IS_STACK_PUSH (to))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
XEXP (XEXP (to, 0), 0),
REG_NOTES (insn));
}
else if (IS_STACK_POP (from))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
XEXP (XEXP (from, 0), 0),
REG_NOTES (insn));
}
 
/* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
to think that sp == _.frame and later replace a x = sp with x = _.frame.
The problem is that we are lying to gcc and use `txs' for x = sp
(which is not really true because txs is really x = sp + 1). */
else if (TARGET_M6811 && SP_REG_P (from))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
from,
REG_NOTES (insn));
}
}
 
int
m68hc11_total_frame_size (void)
{
int size;
int regno;
 
size = get_frame_size ();
if (current_function_interrupt)
{
size += 3 * HARD_REG_SIZE;
}
if (frame_pointer_needed)
size += HARD_REG_SIZE;
 
for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
if (regs_ever_live[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 (current_function_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 (regs_ever_live[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 (current_function_return_rtx == 0)
return_size = 0;
else if (GET_CODE (current_function_return_rtx) == MEM)
return_size = HARD_REG_SIZE;
else
return_size = GET_MODE_SIZE (GET_MODE (current_function_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 (regs_ever_live[regno] && !call_used_regs[regno])
{
emit_move_after_reload (gen_rtx_REG (HImode, regno),
stack_pop_word, scratch);
}
}
 
/* de-allocate auto variables */
if (TARGET_M6812 && (size > 4 || size == 3))
{
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, GEN_INT (size)));
}
else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
{
rtx insn;
 
insn = gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
gen_rtx_PLUS (HImode,
stack_pointer_rtx,
GEN_INT (size))),
gen_rtx_CLOBBER (VOIDmode, scratch)));
emit_insn (insn);
}
else
{
int i;
 
for (i = 2; i <= size; i += 2)
emit_move_after_reload (scratch, stack_pop_word, scratch);
if (size & 1)
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, const1_rtx));
}
 
/* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
if (current_function_interrupt)
{
emit_move_after_reload (gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
stack_pop_word, scratch);
emit_move_after_reload (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
stack_pop_word, scratch);
emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
}
 
/* Restore previous frame pointer. */
if (frame_pointer_needed)
emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
 
/* If the trap handler returns some value, copy the value
in D, X onto the stack so that the rti will pop the return value
correctly. */
else if (current_function_trap && return_size != 0)
{
rtx addr_reg = stack_pointer_rtx;
 
if (!TARGET_M6812)
{
emit_move_after_reload (scratch, stack_pointer_rtx, 0);
addr_reg = scratch;
}
emit_move_after_reload (gen_rtx_MEM (HImode,
gen_rtx_PLUS (HImode, addr_reg,
const1_rtx)), d_reg, 0);
if (return_size > HARD_REG_SIZE)
emit_move_after_reload (gen_rtx_MEM (HImode,
gen_rtx_PLUS (HImode, addr_reg,
GEN_INT (3))), ix_reg, 0);
}
 
emit_jump_insn (gen_return ());
}
 
/* Low and High part extraction for 68HC11. These routines are
similar to gen_lowpart and gen_highpart but they have been
fixed to work for constants and 68HC11 specific registers. */
 
rtx
m68hc11_gen_lowpart (enum machine_mode mode, rtx x)
{
/* We assume that the low part of an auto-inc mode is the same with
the mode changed and that the caller split the larger mode in the
correct order. */
if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
{
return gen_rtx_MEM (mode, XEXP (x, 0));
}
 
/* Note that a CONST_DOUBLE rtx could represent either an integer or a
floating-point constant. A CONST_DOUBLE is used whenever the
constant requires more than one word in order to be adequately
represented. */
if (GET_CODE (x) == CONST_DOUBLE)
{
long l[2];
 
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
REAL_VALUE_TYPE r;
 
if (GET_MODE (x) == SFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
}
else
{
rtx first, second;
 
split_double (x, &first, &second);
return second;
}
if (mode == SImode)
return GEN_INT (l[0]);
 
return gen_int_mode (l[0], HImode);
}
else
{
l[0] = CONST_DOUBLE_LOW (x);
}
switch (mode)
{
case SImode:
return GEN_INT (l[0]);
case HImode:
gcc_assert (GET_MODE (x) == SFmode);
return gen_int_mode (l[0], HImode);
default:
gcc_unreachable ();
}
}
 
if (mode == QImode && D_REG_P (x))
return gen_rtx_REG (mode, HARD_B_REGNUM);
 
/* gen_lowpart crashes when it is called with a SUBREG. */
if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
{
switch (mode)
{
case SImode:
return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
case HImode:
return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
default:
gcc_unreachable ();
}
}
x = gen_lowpart (mode, x);
 
/* Return a different rtx to avoid to share it in several insns
(when used by a split pattern). Sharing addresses within
a MEM breaks the Z register replacement (and reloading). */
if (GET_CODE (x) == MEM)
x = copy_rtx (x);
return x;
}
 
rtx
m68hc11_gen_highpart (enum machine_mode mode, rtx x)
{
/* We assume that the high part of an auto-inc mode is the same with
the mode changed and that the caller split the larger mode in the
correct order. */
if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
{
return gen_rtx_MEM (mode, XEXP (x, 0));
}
 
/* Note that a CONST_DOUBLE rtx could represent either an integer or a
floating-point constant. A CONST_DOUBLE is used whenever the
constant requires more than one word in order to be adequately
represented. */
if (GET_CODE (x) == CONST_DOUBLE)
{
long l[2];
 
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
REAL_VALUE_TYPE r;
 
if (GET_MODE (x) == SFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
}
else
{
rtx first, second;
 
split_double (x, &first, &second);
return first;
}
if (mode == SImode)
return GEN_INT (l[1]);
 
return gen_int_mode ((l[1] >> 16), HImode);
}
else
{
l[1] = CONST_DOUBLE_HIGH (x);
}
 
switch (mode)
{
case SImode:
return GEN_INT (l[1]);
case HImode:
gcc_assert (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
return gen_int_mode ((l[0] >> 16), HImode);
default:
gcc_unreachable ();
}
}
if (GET_CODE (x) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (x);
 
if (mode == QImode)
{
return gen_int_mode (val >> 8, QImode);
}
else if (mode == HImode)
{
return gen_int_mode (val >> 16, HImode);
}
else if (mode == SImode)
{
return gen_int_mode (val >> 32, SImode);
}
}
if (mode == QImode && D_REG_P (x))
return gen_rtx_REG (mode, HARD_A_REGNUM);
 
/* There is no way in GCC to represent the upper part of a word register.
To obtain the 8-bit upper part of a soft register, we change the
reg into a mem rtx. This is possible because they are physically
located in memory. There is no offset because we are big-endian. */
if (mode == QImode && S_REG_P (x))
{
int pos;
 
/* Avoid the '*' for direct addressing mode when this
addressing mode is disabled. */
pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
return gen_rtx_MEM (QImode,
gen_rtx_SYMBOL_REF (Pmode,
&reg_names[REGNO (x)][pos]));
}
 
/* gen_highpart crashes when it is called with a SUBREG. */
switch (GET_CODE (x))
{
case SUBREG:
return gen_rtx_SUBREG (mode, XEXP (x, 0), XEXP (x, 1));
case REG:
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
return gen_rtx_REG (mode, REGNO (x));
else
return gen_rtx_SUBREG (mode, x, 0);
case MEM:
x = change_address (x, mode, 0);
 
/* Return a different rtx to avoid to share it in several insns
(when used by a split pattern). Sharing addresses within
a MEM breaks the Z register replacement (and reloading). */
if (GET_CODE (x) == MEM)
x = copy_rtx (x);
return x;
 
default:
gcc_unreachable ();
}
}
 
/* Obscure register manipulation. */
 
/* Finds backward in the instructions to see if register 'reg' is
dead. This is used when generating code to see if we can use 'reg'
as a scratch register. This allows us to choose a better generation
of code when we know that some register dies or can be clobbered. */
 
int
dead_register_here (rtx x, rtx reg)
{
rtx x_reg;
rtx p;
 
if (D_REG_P (reg))
x_reg = gen_rtx_REG (SImode, HARD_X_REGNUM);
else
x_reg = 0;
 
for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
if (INSN_P (p))
{
rtx body;
 
body = PATTERN (p);
 
if (GET_CODE (body) == CALL_INSN)
break;
if (GET_CODE (body) == JUMP_INSN)
break;
 
if (GET_CODE (body) == SET)
{
rtx dst = XEXP (body, 0);
 
if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
break;
if (x_reg && rtx_equal_p (dst, x_reg))
break;
 
if (find_regno_note (p, REG_DEAD, REGNO (reg)))
return 1;
}
else if (reg_mentioned_p (reg, p)
|| (x_reg && reg_mentioned_p (x_reg, p)))
break;
}
 
/* Scan forward to see if the register is set in some insns and never
used since then. */
for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
{
rtx body;
 
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
break;
 
if (GET_CODE (p) != INSN)
continue;
 
body = PATTERN (p);
if (GET_CODE (body) == SET)
{
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
 
if (GET_CODE (dst) == REG
&& REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
return 1;
}
 
/* Register is used (may be in source or in dest). */
if (reg_mentioned_p (reg, p)
|| (x_reg != 0 && GET_MODE (p) == SImode
&& reg_mentioned_p (x_reg, p)))
break;
}
return p == 0 ? 1 : 0;
}
 
/* Code generation operations called from machine description file. */
 
/* Print the name of register 'regno' in the assembly file. */
static void
asm_print_register (FILE *file, int regno)
{
const char *name = reg_names[regno];
 
if (TARGET_NO_DIRECT_MODE && name[0] == '*')
name++;
 
fprintf (file, "%s", name);
}
 
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand X. X is an RTL
expression.
 
CODE is a value that can be used to specify one of several ways
of printing the operand. It is used when identical operands
must be printed differently depending on the context. CODE
comes from the `%' specification that was used to request
printing of the operand. If the specification was just `%DIGIT'
then CODE is 0; if the specification was `%LTR DIGIT' then CODE
is the ASCII code for LTR.
 
If X is a register, this macro should print the register's name.
The names can be found in an array `reg_names' whose type is
`char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
 
When the machine description has a specification `%PUNCT' (a `%'
followed by a punctuation character), this macro is called with
a null pointer for X and the punctuation character for CODE.
 
The M68HC11 specific codes are:
 
'b' for the low part of the operand.
'h' for the high part of the operand
The 'b' or 'h' modifiers have no effect if the operand has
the QImode and is not a S_REG_P (soft register). If the
operand is a hard register, these two modifiers have no effect.
't' generate the temporary scratch register. The operand is
ignored.
'T' generate the low-part temporary scratch register. The operand is
ignored. */
 
void
print_operand (FILE *file, rtx op, int letter)
{
if (letter == 't')
{
asm_print_register (file, SOFT_TMP_REGNUM);
return;
}
else if (letter == 'T')
{
asm_print_register (file, SOFT_TMP_REGNUM);
fprintf (file, "+1");
return;
}
else if (letter == '#')
{
asm_fprintf (file, "%I");
}
 
if (GET_CODE (op) == REG)
{
if (letter == 'b' && S_REG_P (op))
{
asm_print_register (file, REGNO (op));
fprintf (file, "+1");
}
else if (letter == 'b' && D_REG_P (op))
{
asm_print_register (file, HARD_B_REGNUM);
}
else
{
asm_print_register (file, REGNO (op));
}
return;
}
 
if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
{
if (letter == 'b')
asm_fprintf (file, "%I%%lo(");
else
asm_fprintf (file, "%I%%hi(");
 
output_addr_const (file, op);
fprintf (file, ")");
return;
}
 
/* Get the low or high part of the operand when 'b' or 'h' modifiers
are specified. If we already have a QImode, there is nothing to do. */
if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
{
if (letter == 'b')
{
op = m68hc11_gen_lowpart (QImode, op);
}
else if (letter == 'h')
{
op = m68hc11_gen_highpart (QImode, op);
}
}
 
if (GET_CODE (op) == MEM)
{
rtx base = XEXP (op, 0);
switch (GET_CODE (base))
{
case PRE_DEC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
break;
 
case POST_DEC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "-");
break;
 
case POST_INC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "+");
break;
 
case PRE_INC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
break;
 
case MEM:
gcc_assert (TARGET_M6812);
fprintf (file, "[");
print_operand_address (file, XEXP (base, 0));
fprintf (file, "]");
break;
 
default:
if (m68hc11_page0_symbol_p (base))
fprintf (file, "*");
 
output_address (base);
break;
}
}
else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
{
REAL_VALUE_TYPE r;
long l;
 
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
REAL_VALUE_TO_TARGET_SINGLE (r, l);
asm_fprintf (file, "%I0x%lx", l);
}
else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
{
char dstr[30];
 
real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
sizeof (dstr), 0, 1);
asm_fprintf (file, "%I0r%s", dstr);
}
else
{
int need_parenthesize = 0;
 
if (letter != 'i')
asm_fprintf (file, "%I");
else
need_parenthesize = must_parenthesize (op);
 
if (need_parenthesize)
fprintf (file, "(");
 
output_addr_const (file, op);
if (need_parenthesize)
fprintf (file, ")");
}
}
 
/* Returns true if the operand 'op' must be printed with parenthesis
around it. This must be done only if there is a symbol whose name
is a processor register. */
static int
must_parenthesize (rtx op)
{
const char *name;
 
switch (GET_CODE (op))
{
case SYMBOL_REF:
name = XSTR (op, 0);
/* Avoid a conflict between symbol name and a possible
register. */
return (strcasecmp (name, "a") == 0
|| strcasecmp (name, "b") == 0
|| strcasecmp (name, "d") == 0
|| strcasecmp (name, "x") == 0
|| strcasecmp (name, "y") == 0
|| strcasecmp (name, "ix") == 0
|| strcasecmp (name, "iy") == 0
|| strcasecmp (name, "pc") == 0
|| strcasecmp (name, "sp") == 0
|| strcasecmp (name, "ccr") == 0) ? 1 : 0;
 
case PLUS:
case MINUS:
return must_parenthesize (XEXP (op, 0))
|| must_parenthesize (XEXP (op, 1));
 
case MEM:
case CONST:
case ZERO_EXTEND:
case SIGN_EXTEND:
return must_parenthesize (XEXP (op, 0));
 
case CONST_DOUBLE:
case CONST_INT:
case LABEL_REF:
case CODE_LABEL:
default:
return 0;
}
}
 
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand that is a memory
reference whose address is ADDR. ADDR is an RTL expression. */
 
void
print_operand_address (FILE *file, rtx addr)
{
rtx base;
rtx offset;
int need_parenthesis = 0;
 
switch (GET_CODE (addr))
{
case REG:
gcc_assert (REG_P (addr) && REG_OK_FOR_BASE_STRICT_P (addr));
 
fprintf (file, "0,");
asm_print_register (file, REGNO (addr));
break;
 
case MEM:
base = XEXP (addr, 0);
switch (GET_CODE (base))
{
case PRE_DEC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
break;
 
case POST_DEC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "-");
break;
 
case POST_INC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "+");
break;
 
case PRE_INC:
gcc_assert (TARGET_M6812);
fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
break;
 
default:
need_parenthesis = must_parenthesize (base);
if (need_parenthesis)
fprintf (file, "(");
 
output_addr_const (file, base);
if (need_parenthesis)
fprintf (file, ")");
break;
}
break;
 
case PLUS:
base = XEXP (addr, 0);
offset = XEXP (addr, 1);
if (!G_REG_P (base) && G_REG_P (offset))
{
base = XEXP (addr, 1);
offset = XEXP (addr, 0);
}
if (CONSTANT_ADDRESS_P (base))
{
need_parenthesis = must_parenthesize (addr);
 
gcc_assert (CONSTANT_ADDRESS_P (offset));
if (need_parenthesis)
fprintf (file, "(");
 
output_addr_const (file, base);
fprintf (file, "+");
output_addr_const (file, offset);
if (need_parenthesis)
fprintf (file, ")");
}
else
{
gcc_assert (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base));
if (REG_P (offset))
{
gcc_assert (TARGET_M6812);
asm_print_register (file, REGNO (offset));
fprintf (file, ",");
asm_print_register (file, REGNO (base));
}
else
{
need_parenthesis = must_parenthesize (offset);
if (need_parenthesis)
fprintf (file, "(");
 
output_addr_const (file, offset);
if (need_parenthesis)
fprintf (file, ")");
fprintf (file, ",");
asm_print_register (file, REGNO (base));
}
}
break;
 
default:
if (GET_CODE (addr) == CONST_INT
&& INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
{
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
}
else
{
need_parenthesis = must_parenthesize (addr);
if (need_parenthesis)
fprintf (file, "(");
 
output_addr_const (file, addr);
if (need_parenthesis)
fprintf (file, ")");
}
break;
}
}
 
/* Splitting of some instructions. */
 
static rtx
m68hc11_expand_compare (enum rtx_code code, rtx op0, rtx op1)
{
rtx ret = 0;
 
gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT);
emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
gen_rtx_COMPARE (VOIDmode, op0, op1)));
ret = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
 
return ret;
}
 
rtx
m68hc11_expand_compare_and_branch (enum rtx_code code, rtx op0, rtx op1,
rtx label)
{
rtx tmp;
 
switch (GET_MODE (op0))
{
case QImode:
case HImode:
tmp = m68hc11_expand_compare (code, op0, op1);
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
return 0;
#if 0
 
/* SCz: from i386.c */
case SFmode:
case DFmode:
/* Don't expand the comparison early, so that we get better code
when jump or whoever decides to reverse the comparison. */
{
rtvec vec;
int use_fcomi;
 
code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
&m68hc11_compare_op1);
 
tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
m68hc11_compare_op0, m68hc11_compare_op1);
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx);
tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
 
use_fcomi = ix86_use_fcomi_compare (code);
vec = rtvec_alloc (3 + !use_fcomi);
RTVEC_ELT (vec, 0) = tmp;
RTVEC_ELT (vec, 1)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
RTVEC_ELT (vec, 2)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
if (!use_fcomi)
RTVEC_ELT (vec, 3)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
 
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
return;
}
#endif
 
case SImode:
/* Expand SImode branch into multiple compare+branch. */
{
rtx lo[2], hi[2], label2;
enum rtx_code code1, code2, code3;
 
if (CONSTANT_P (op0) && !CONSTANT_P (op1))
{
tmp = op0;
op0 = op1;
op1 = tmp;
code = swap_condition (code);
}
lo[0] = m68hc11_gen_lowpart (HImode, op0);
lo[1] = m68hc11_gen_lowpart (HImode, op1);
hi[0] = m68hc11_gen_highpart (HImode, op0);
hi[1] = m68hc11_gen_highpart (HImode, op1);
 
/* Otherwise, if we are doing less-than, op1 is a constant and the
low word is zero, then we can just examine the high word. */
 
if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
&& (code == LT || code == LTU))
{
return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
label);
}
 
/* Otherwise, we need two or three jumps. */
 
label2 = gen_label_rtx ();
 
code1 = code;
code2 = swap_condition (code);
code3 = unsigned_condition (code);
 
switch (code)
{
case LT:
case GT:
case LTU:
case GTU:
break;
 
case LE:
code1 = LT;
code2 = GT;
break;
case GE:
code1 = GT;
code2 = LT;
break;
case LEU:
code1 = LTU;
code2 = GTU;
break;
case GEU:
code1 = GTU;
code2 = LTU;
break;
 
case EQ:
code1 = UNKNOWN;
code2 = NE;
break;
case NE:
code2 = UNKNOWN;
break;
 
default:
gcc_unreachable ();
}
 
/*
* a < b =>
* if (hi(a) < hi(b)) goto true;
* if (hi(a) > hi(b)) goto false;
* if (lo(a) < lo(b)) goto true;
* false:
*/
if (code1 != UNKNOWN)
m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
if (code2 != UNKNOWN)
m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
 
m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
 
if (code2 != UNKNOWN)
emit_label (label2);
return 0;
}
 
default:
gcc_unreachable ();
}
return 0;
}
 
/* Return the increment/decrement mode of a MEM if it is such.
Return CONST if it is anything else. */
static int
autoinc_mode (rtx x)
{
if (GET_CODE (x) != MEM)
return CONST;
 
x = XEXP (x, 0);
if (GET_CODE (x) == PRE_INC
|| GET_CODE (x) == PRE_DEC
|| GET_CODE (x) == POST_INC
|| GET_CODE (x) == POST_DEC)
return GET_CODE (x);
 
return CONST;
}
 
static int
m68hc11_make_autoinc_notes (rtx *x, void *data)
{
rtx insn;
switch (GET_CODE (*x))
{
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
insn = (rtx) data;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
REG_NOTES (insn));
return -1;
 
default:
return 0;
}
}
 
/* Split a DI, SI or HI move into several smaller move operations.
The scratch register 'scratch' is used as a temporary to load
store intermediate values. It must be a hard register. */
void
m68hc11_split_move (rtx to, rtx from, rtx scratch)
{
rtx low_to, low_from;
rtx high_to, high_from;
rtx insn;
enum machine_mode mode;
int offset = 0;
int autoinc_from = autoinc_mode (from);
int autoinc_to = autoinc_mode (to);
 
mode = GET_MODE (to);
 
/* If the TO and FROM contain autoinc modes that are not compatible
together (one pop and the other a push), we must change one to
an offsetable operand and generate an appropriate add at the end. */
if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
{
rtx reg;
int code;
 
/* The source uses an autoinc mode which is not compatible with
a split (this would result in a word swap). */
if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
{
code = GET_CODE (XEXP (from, 0));
reg = XEXP (XEXP (from, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (from));
if (code == POST_DEC)
offset = -offset;
 
if (code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
 
m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
if (code == POST_DEC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
 
/* Likewise for destination. */
if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
{
code = GET_CODE (XEXP (to, 0));
reg = XEXP (XEXP (to, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (to));
if (code == POST_DEC)
offset = -offset;
 
if (code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
 
m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
if (code == POST_DEC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
 
/* The source and destination auto increment modes must be compatible
with each other: same direction. */
if ((autoinc_to != autoinc_from
&& autoinc_to != CONST && autoinc_from != CONST)
/* The destination address register must not be used within
the source operand because the source address would change
while doing the copy. */
|| (autoinc_to != CONST
&& reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
&& !IS_STACK_PUSH (to)))
{
/* Must change the destination. */
code = GET_CODE (XEXP (to, 0));
reg = XEXP (XEXP (to, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (to));
if (code == PRE_DEC || code == POST_DEC)
offset = -offset;
 
if (code == PRE_DEC || code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
if (code == POST_DEC || code == POST_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
 
return;
}
 
/* Likewise, the source address register must not be used within
the destination operand. */
if (autoinc_from != CONST
&& reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
&& !IS_STACK_PUSH (to))
{
/* Must change the source. */
code = GET_CODE (XEXP (from, 0));
reg = XEXP (XEXP (from, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (from));
if (code == PRE_DEC || code == POST_DEC)
offset = -offset;
 
if (code == PRE_DEC || code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
if (code == POST_DEC || code == POST_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
 
return;
}
}
 
if (GET_MODE_SIZE (mode) == 8)
mode = SImode;
else if (GET_MODE_SIZE (mode) == 4)
mode = HImode;
else
mode = QImode;
 
if (TARGET_M6812
&& IS_STACK_PUSH (to)
&& reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM), from))
{
if (mode == SImode)
{
offset = 4;
}
else if (mode == HImode)
{
offset = 2;
}
else
offset = 0;
}
 
low_to = m68hc11_gen_lowpart (mode, to);
high_to = m68hc11_gen_highpart (mode, to);
 
low_from = m68hc11_gen_lowpart (mode, from);
high_from = m68hc11_gen_highpart (mode, from);
 
if (offset)
{
high_from = adjust_address (high_from, mode, offset);
low_from = high_from;
}
 
/* When copying with a POST_INC mode, we must copy the
high part and then the low part to guarantee a correct
32/64-bit copy. */
if (TARGET_M6812
&& GET_MODE_SIZE (mode) >= 2
&& autoinc_from != autoinc_to
&& (autoinc_from == POST_INC || autoinc_to == POST_INC))
{
rtx swap;
 
swap = low_to;
low_to = high_to;
high_to = swap;
 
swap = low_from;
low_from = high_from;
high_from = swap;
}
if (mode == SImode)
{
m68hc11_split_move (low_to, low_from, scratch);
m68hc11_split_move (high_to, high_from, scratch);
}
else if (H_REG_P (to) || H_REG_P (from)
|| (low_from == const0_rtx
&& high_from == const0_rtx
&& ! push_operand (to, GET_MODE (to))
&& ! H_REG_P (scratch))
|| (TARGET_M6812
&& (!m68hc11_register_indirect_p (from, GET_MODE (from))
|| m68hc11_small_indexed_indirect_p (from,
GET_MODE (from)))
&& (!m68hc11_register_indirect_p (to, GET_MODE (to))
|| m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
{
insn = emit_move_insn (low_to, low_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
 
insn = emit_move_insn (high_to, high_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
}
else
{
insn = emit_move_insn (scratch, low_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (low_to, scratch);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
 
insn = emit_move_insn (scratch, high_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (high_to, scratch);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
}
}
 
static rtx
simplify_logical (enum machine_mode mode, int code, rtx operand, rtx *result)
{
int val;
int mask;
 
*result = 0;
if (GET_CODE (operand) != CONST_INT)
return operand;
 
if (mode == HImode)
mask = 0x0ffff;
else
mask = 0x0ff;
 
val = INTVAL (operand);
switch (code)
{
case IOR:
if ((val & mask) == 0)
return 0;
if ((val & mask) == mask)
*result = constm1_rtx;
break;
 
case AND:
if ((val & mask) == 0)
*result = const0_rtx;
if ((val & mask) == mask)
return 0;
break;
 
case XOR:
if ((val & mask) == 0)
return 0;
break;
}
return operand;
}
 
static void
m68hc11_emit_logical (enum machine_mode mode, int code, rtx *operands)
{
rtx result;
int need_copy;
 
need_copy = (rtx_equal_p (operands[0], operands[1])
|| rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
 
operands[1] = simplify_logical (mode, code, operands[1], &result);
operands[2] = simplify_logical (mode, code, operands[2], &result);
 
if (result && GET_CODE (result) == CONST_INT)
{
if (!H_REG_P (operands[0]) && operands[3]
&& (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
{
emit_move_insn (operands[3], result);
emit_move_insn (operands[0], operands[3]);
}
else
{
emit_move_insn (operands[0], result);
}
}
else if (operands[1] != 0 && operands[2] != 0)
{
rtx insn;
 
if (!H_REG_P (operands[0]) && operands[3])
{
emit_move_insn (operands[3], operands[1]);
emit_insn (gen_rtx_SET (mode,
operands[3],
gen_rtx_fmt_ee (code, mode,
operands[3], operands[2])));
insn = emit_move_insn (operands[0], operands[3]);
}
else
{
insn = emit_insn (gen_rtx_SET (mode,
operands[0],
gen_rtx_fmt_ee (code, mode,
operands[0],
operands[2])));
}
}
 
/* The logical operation is similar to a copy. */
else if (need_copy)
{
rtx src;
 
if (GET_CODE (operands[1]) == CONST_INT)
src = operands[2];
else
src = operands[1];
 
if (!H_REG_P (operands[0]) && !H_REG_P (src))
{
emit_move_insn (operands[3], src);
emit_move_insn (operands[0], operands[3]);
}
else
{
emit_move_insn (operands[0], src);
}
}
}
 
void
m68hc11_split_logical (enum machine_mode mode, int code, rtx *operands)
{
rtx low[4];
rtx high[4];
 
low[0] = m68hc11_gen_lowpart (mode, operands[0]);
low[1] = m68hc11_gen_lowpart (mode, operands[1]);
low[2] = m68hc11_gen_lowpart (mode, operands[2]);
 
high[0] = m68hc11_gen_highpart (mode, operands[0]);
high[1] = m68hc11_gen_highpart (mode, operands[1]);
high[2] = m68hc11_gen_highpart (mode, operands[2]);
 
low[3] = operands[3];
high[3] = operands[3];
if (mode == SImode)
{
m68hc11_split_logical (HImode, code, low);
m68hc11_split_logical (HImode, code, high);
return;
}
 
m68hc11_emit_logical (mode, code, low);
m68hc11_emit_logical (mode, code, high);
}
 
/* Code generation. */
 
void
m68hc11_output_swap (rtx insn ATTRIBUTE_UNUSED, rtx operands[])
{
/* We have to be careful with the cc_status. An address register swap
is generated for some comparison. The comparison is made with D
but the branch really uses the address register. See the split
pattern for compare. The xgdx/xgdy preserve the flags but after
the exchange, the flags will reflect to the value of X and not D.
Tell this by setting the cc_status according to the cc_prev_status. */
if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
{
if (cc_prev_status.value1 != 0
&& (D_REG_P (cc_prev_status.value1)
|| X_REG_P (cc_prev_status.value1)))
{
cc_status = cc_prev_status;
if (D_REG_P (cc_status.value1))
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_X_REGNUM);
else
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_D_REGNUM);
}
else
CC_STATUS_INIT;
 
output_asm_insn ("xgdx", operands);
}
else
{
if (cc_prev_status.value1 != 0
&& (D_REG_P (cc_prev_status.value1)
|| Y_REG_P (cc_prev_status.value1)))
{
cc_status = cc_prev_status;
if (D_REG_P (cc_status.value1))
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_Y_REGNUM);
else
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_D_REGNUM);
}
else
CC_STATUS_INIT;
 
output_asm_insn ("xgdy", operands);
}
}
 
/* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
This is used to decide whether a move that set flags should be used
instead. */
int
next_insn_test_reg (rtx insn, rtx reg)
{
rtx body;
 
insn = next_nonnote_insn (insn);
if (GET_CODE (insn) != INSN)
return 0;
 
body = PATTERN (insn);
if (sets_cc0_p (body) != 1)
return 0;
 
if (rtx_equal_p (XEXP (body, 1), reg) == 0)
return 0;
 
return 1;
}
 
/* Generate the code to move a 16-bit operand into another one. */
 
void
m68hc11_gen_movhi (rtx insn, rtx *operands)
{
int reg;
 
/* Move a register or memory to the same location.
This is possible because such insn can appear
in a non-optimizing mode. */
if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
{
cc_status = cc_prev_status;
return;
}
 
if (TARGET_M6812)
{
rtx from = operands[1];
rtx to = operands[0];
 
if (IS_STACK_PUSH (to) && H_REG_P (from))
{
cc_status = cc_prev_status;
switch (REGNO (from))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
case HARD_D_REGNUM:
output_asm_insn ("psh%1", operands);
break;
case HARD_SP_REGNUM:
output_asm_insn ("sts\t2,-sp", operands);
break;
default:
gcc_unreachable ();
}
return;
}
if (IS_STACK_POP (from) && H_REG_P (to))
{
cc_status = cc_prev_status;
switch (REGNO (to))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
case HARD_D_REGNUM:
output_asm_insn ("pul%0", operands);
break;
default:
gcc_unreachable ();
}
return;
}
if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("tfr\t%1,%0", operands);
}
else if (H_REG_P (operands[0]))
{
if (SP_REG_P (operands[0]))
output_asm_insn ("lds\t%1", operands);
else
output_asm_insn ("ld%0\t%1", operands);
}
else if (H_REG_P (operands[1]))
{
if (SP_REG_P (operands[1]))
output_asm_insn ("sts\t%0", operands);
else
output_asm_insn ("st%1\t%0", operands);
}
 
/* The 68hc12 does not support (MEM:HI (MEM:HI)) with the movw
instruction. We have to use a scratch register as temporary location.
Trying to use a specific pattern or constrain failed. */
else if (GET_CODE (to) == MEM && GET_CODE (XEXP (to, 0)) == MEM)
{
rtx ops[4];
 
ops[0] = to;
ops[2] = from;
ops[3] = 0;
if (dead_register_here (insn, d_reg))
ops[1] = d_reg;
else if (dead_register_here (insn, ix_reg))
ops[1] = ix_reg;
else if (dead_register_here (insn, iy_reg))
ops[1] = iy_reg;
else
{
ops[1] = d_reg;
ops[3] = d_reg;
output_asm_insn ("psh%3", ops);
}
 
ops[0] = to;
ops[2] = from;
output_asm_insn ("ld%1\t%2", ops);
output_asm_insn ("st%1\t%0", ops);
if (ops[3])
output_asm_insn ("pul%3", ops);
}
 
/* Use movw for non-null constants or when we are clearing
a volatile memory reference. However, this is possible
only if the memory reference has a small offset or is an
absolute address. */
else if (GET_CODE (from) == CONST_INT
&& INTVAL (from) == 0
&& (MEM_VOLATILE_P (to) == 0
|| m68hc11_small_indexed_indirect_p (to, HImode) == 0))
{
output_asm_insn ("clr\t%h0", operands);
output_asm_insn ("clr\t%b0", operands);
}
else
{
if ((m68hc11_register_indirect_p (from, GET_MODE (from))
&& !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
|| (m68hc11_register_indirect_p (to, GET_MODE (to))
&& !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
{
rtx ops[3];
 
if (operands[2])
{
ops[0] = operands[2];
ops[1] = from;
ops[2] = 0;
m68hc11_gen_movhi (insn, ops);
ops[0] = to;
ops[1] = operands[2];
m68hc11_gen_movhi (insn, ops);
return;
}
else
{
/* !!!! SCz wrong here. */
fatal_insn ("move insn not handled", insn);
}
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("movw\t%1,%0", operands);
}
}
return;
}
 
if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
{
cc_status = cc_prev_status;
switch (REGNO (operands[0]))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
output_asm_insn ("pul%0", operands);
break;
case HARD_D_REGNUM:
output_asm_insn ("pula", operands);
output_asm_insn ("pulb", operands);
break;
default:
gcc_unreachable ();
}
return;
}
/* Some moves to a hard register are special. Not all of them
are really supported and we have to use a temporary
location to provide them (either the stack of a temp var). */
if (H_REG_P (operands[0]))
{
switch (REGNO (operands[0]))
{
case HARD_D_REGNUM:
if (X_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else if (next_insn_test_reg (insn, operands[0]))
{
output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
}
}
else if (Y_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
/* %t means *ZTMP scratch register. */
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldd\t%t1", operands);
}
}
else if (SP_REG_P (operands[1]))
{
CC_STATUS_INIT;
if (ix_reg == 0)
create_regs_rtx ();
if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
output_asm_insn ("xgdx", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("xgdx", operands);
}
else if (IS_STACK_POP (operands[1]))
{
output_asm_insn ("pula\n\tpulb", operands);
}
else if (GET_CODE (operands[1]) == CONST_INT
&& INTVAL (operands[1]) == 0)
{
output_asm_insn ("clra\n\tclrb", operands);
}
else
{
output_asm_insn ("ldd\t%1", operands);
}
break;
 
case HARD_X_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else if (next_insn_test_reg (insn, operands[0]))
{
output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("pshb", operands);
output_asm_insn ("psha", operands);
output_asm_insn ("pulx", operands);
}
}
else if (Y_REG_P (operands[1]))
{
/* When both D and Y are dead, use the sequence xgdy, xgdx
to move Y into X. The D and Y registers are modified. */
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
&& dead_register_here (insn, d_reg))
{
output_asm_insn ("xgdy", operands);
output_asm_insn ("xgdx", operands);
CC_STATUS_INIT;
}
else if (!optimize_size)
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldx\t%t1", operands);
}
else
{
CC_STATUS_INIT;
output_asm_insn ("pshy", operands);
output_asm_insn ("pulx", operands);
}
}
else if (SP_REG_P (operands[1]))
{
/* tsx, tsy preserve the flags */
cc_status = cc_prev_status;
output_asm_insn ("tsx", operands);
}
else
{
output_asm_insn ("ldx\t%1", operands);
}
break;
 
case HARD_Y_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("std\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
}
}
else if (X_REG_P (operands[1]))
{
/* When both D and X are dead, use the sequence xgdx, xgdy
to move X into Y. The D and X registers are modified. */
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
&& dead_register_here (insn, d_reg))
{
output_asm_insn ("xgdx", operands);
output_asm_insn ("xgdy", operands);
CC_STATUS_INIT;
}
else if (!optimize_size)
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
}
else
{
CC_STATUS_INIT;
output_asm_insn ("pshx", operands);
output_asm_insn ("puly", operands);
}
}
else if (SP_REG_P (operands[1]))
{
/* tsx, tsy preserve the flags */
cc_status = cc_prev_status;
output_asm_insn ("tsy", operands);
}
else
{
output_asm_insn ("ldy\t%1", operands);
}
break;
 
case HARD_SP_REGNUM:
if (D_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("xgdx", operands);
output_asm_insn ("txs", operands);
output_asm_insn ("xgdx", operands);
}
else if (X_REG_P (operands[1]))
{
/* tys, txs preserve the flags */
cc_status = cc_prev_status;
output_asm_insn ("txs", operands);
}
else if (Y_REG_P (operands[1]))
{
/* tys, txs preserve the flags */
cc_status = cc_prev_status;
output_asm_insn ("tys", operands);
}
else
{
/* lds sets the flags but the des does not. */
CC_STATUS_INIT;
output_asm_insn ("lds\t%1", operands);
output_asm_insn ("des", operands);
}
break;
 
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
return;
}
if (SP_REG_P (operands[1]) && REG_P (operands[0])
&& REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
{
output_asm_insn ("sts\t%0", operands);
return;
}
 
if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
{
cc_status = cc_prev_status;
switch (REGNO (operands[1]))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
output_asm_insn ("psh%1", operands);
break;
case HARD_D_REGNUM:
output_asm_insn ("pshb", operands);
output_asm_insn ("psha", operands);
break;
default:
gcc_unreachable ();
}
return;
}
 
/* Operand 1 must be a hard register. */
if (!H_REG_P (operands[1]))
{
fatal_insn ("invalid operand in the instruction", insn);
}
 
reg = REGNO (operands[1]);
switch (reg)
{
case HARD_D_REGNUM:
output_asm_insn ("std\t%0", operands);
break;
 
case HARD_X_REGNUM:
output_asm_insn ("stx\t%0", operands);
break;
 
case HARD_Y_REGNUM:
output_asm_insn ("sty\t%0", operands);
break;
 
case HARD_SP_REGNUM:
if (ix_reg == 0)
create_regs_rtx ();
 
if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
{
output_asm_insn ("pshx", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("inx", operands);
output_asm_insn ("inx", operands);
output_asm_insn ("stx\t%0", operands);
output_asm_insn ("pulx", operands);
}
else if (reg_mentioned_p (ix_reg, operands[0]))
{
output_asm_insn ("sty\t%t0", operands);
output_asm_insn ("tsy", operands);
output_asm_insn ("sty\t%0", operands);
output_asm_insn ("ldy\t%t0", operands);
}
else
{
output_asm_insn ("stx\t%t0", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("stx\t%0", operands);
output_asm_insn ("ldx\t%t0", operands);
}
CC_STATUS_INIT;
break;
 
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
}
 
void
m68hc11_gen_movqi (rtx insn, rtx *operands)
{
/* Move a register or memory to the same location.
This is possible because such insn can appear
in a non-optimizing mode. */
if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
{
cc_status = cc_prev_status;
return;
}
 
if (TARGET_M6812)
{
 
if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("tfr\t%1,%0", operands);
}
else if (H_REG_P (operands[0]))
{
if (IS_STACK_POP (operands[1]))
output_asm_insn ("pul%b0", operands);
else if (Q_REG_P (operands[0]))
output_asm_insn ("lda%0\t%b1", operands);
else if (D_REG_P (operands[0]))
output_asm_insn ("ldab\t%b1", operands);
else
goto m6811_move;
}
else if (H_REG_P (operands[1]))
{
if (Q_REG_P (operands[1]))
output_asm_insn ("sta%1\t%b0", operands);
else if (D_REG_P (operands[1]))
output_asm_insn ("stab\t%b0", operands);
else
goto m6811_move;
}
else
{
rtx from = operands[1];
rtx to = operands[0];
 
if ((m68hc11_register_indirect_p (from, GET_MODE (from))
&& !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
|| (m68hc11_register_indirect_p (to, GET_MODE (to))
&& !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
{
rtx ops[3];
 
if (operands[2])
{
ops[0] = operands[2];
ops[1] = from;
ops[2] = 0;
m68hc11_gen_movqi (insn, ops);
ops[0] = to;
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
}
else
{
/* !!!! SCz wrong here. */
fatal_insn ("move insn not handled", insn);
}
}
else
{
if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
{
output_asm_insn ("clr\t%b0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("movb\t%b1,%b0", operands);
}
}
}
return;
}
 
m6811_move:
if (H_REG_P (operands[0]))
{
switch (REGNO (operands[0]))
{
case HARD_B_REGNUM:
case HARD_D_REGNUM:
if (X_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldab\t%T0", operands);
}
}
else if (Y_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldab\t%T0", operands);
}
}
else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
&& !DA_REG_P (operands[1]))
{
output_asm_insn ("ldab\t%b1", operands);
}
else if (DA_REG_P (operands[1]))
{
output_asm_insn ("tab", operands);
}
else
{
cc_status = cc_prev_status;
return;
}
break;
 
case HARD_A_REGNUM:
if (X_REG_P (operands[1]))
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldaa\t%T0", operands);
}
else if (Y_REG_P (operands[1]))
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldaa\t%T0", operands);
}
else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
&& !DA_REG_P (operands[1]))
{
output_asm_insn ("ldaa\t%b1", operands);
}
else if (!DA_REG_P (operands[1]))
{
output_asm_insn ("tba", operands);
}
else
{
cc_status = cc_prev_status;
}
break;
 
case HARD_X_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldx\t%t1", operands);
}
CC_STATUS_INIT;
}
else if (Y_REG_P (operands[1]))
{
output_asm_insn ("sty\t%t0", operands);
output_asm_insn ("ldx\t%t0", operands);
}
else if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("ldx\t%1", operands);
}
else if (dead_register_here (insn, d_reg))
{
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdx", operands);
}
else if (!reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("xgdx", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdx", operands);
}
else
{
output_asm_insn ("pshb", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldx\t%t1", operands);
output_asm_insn ("pulb", operands);
CC_STATUS_INIT;
}
break;
 
case HARD_Y_REGNUM:
if (D_REG_P (operands[1]))
{
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldy\t%t1", operands);
CC_STATUS_INIT;
}
else if (X_REG_P (operands[1]))
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
CC_STATUS_INIT;
}
else if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("ldy\t%1", operands);
}
else if (dead_register_here (insn, d_reg))
{
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdy", operands);
}
else if (!reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("xgdy", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdy", operands);
}
else
{
output_asm_insn ("pshb", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldy\t%t1", operands);
output_asm_insn ("pulb", operands);
CC_STATUS_INIT;
}
break;
 
default:
fatal_insn ("invalid register in the instruction", insn);
break;
}
}
else if (H_REG_P (operands[1]))
{
switch (REGNO (operands[1]))
{
case HARD_D_REGNUM:
case HARD_B_REGNUM:
output_asm_insn ("stab\t%b0", operands);
break;
 
case HARD_A_REGNUM:
output_asm_insn ("staa\t%b0", operands);
break;
 
case HARD_X_REGNUM:
output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
break;
 
case HARD_Y_REGNUM:
output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
break;
 
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
return;
}
else
{
fatal_insn ("operand 1 must be a hard register", insn);
}
}
 
/* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
The source and destination must be D or A and the shift must
be a constant. */
void
m68hc11_gen_rotate (enum rtx_code code, rtx insn, rtx operands[])
{
int val;
if (GET_CODE (operands[2]) != CONST_INT
|| (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
fatal_insn ("invalid rotate insn", insn);
 
val = INTVAL (operands[2]);
if (code == ROTATERT)
val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
 
if (GET_MODE (operands[0]) != QImode)
CC_STATUS_INIT;
 
/* Rotate by 8-bits if the shift is within [5..11]. */
if (val >= 5 && val <= 11)
{
if (TARGET_M6812)
output_asm_insn ("exg\ta,b", operands);
else
{
output_asm_insn ("psha", operands);
output_asm_insn ("tba", operands);
output_asm_insn ("pulb", operands);
}
val -= 8;
}
 
/* If the shift is big, invert the rotation. */
else if (val >= 12)
{
val = val - 16;
}
 
if (val > 0)
{
while (--val >= 0)
{
/* Set the carry to bit-15, but don't change D yet. */
if (GET_MODE (operands[0]) != QImode)
{
output_asm_insn ("asra", operands);
output_asm_insn ("rola", operands);
}
 
/* Rotate B first to move the carry to bit-0. */
if (D_REG_P (operands[0]))
output_asm_insn ("rolb", operands);
 
if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
output_asm_insn ("rola", operands);
}
}
else
{
while (++val <= 0)
{
/* Set the carry to bit-8 of D. */
if (GET_MODE (operands[0]) != QImode)
output_asm_insn ("tap", operands);
 
/* Rotate B first to move the carry to bit-7. */
if (D_REG_P (operands[0]))
output_asm_insn ("rorb", operands);
 
if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
output_asm_insn ("rora", operands);
}
}
}
 
 
/* Store in cc_status the expressions that the condition codes will
describe after execution of an instruction whose pattern is EXP.
Do not alter them if the instruction would not alter the cc's. */
 
void
m68hc11_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
{
/* recognize SET insn's. */
if (GET_CODE (exp) == SET)
{
/* Jumps do not alter the cc's. */
if (SET_DEST (exp) == pc_rtx)
;
 
/* NOTE: most instructions don't affect the carry bit, but the
bhi/bls/bhs/blo instructions use it. This isn't mentioned in
the conditions.h header. */
 
/* Function calls clobber the cc's. */
else if (GET_CODE (SET_SRC (exp)) == CALL)
{
CC_STATUS_INIT;
}
 
/* Tests and compares set the cc's in predictable ways. */
else if (SET_DEST (exp) == cc0_rtx)
{
cc_status.flags = 0;
cc_status.value1 = XEXP (exp, 0);
cc_status.value2 = XEXP (exp, 1);
}
else
{
/* All other instructions affect the condition codes. */
cc_status.flags = 0;
cc_status.value1 = XEXP (exp, 0);
cc_status.value2 = XEXP (exp, 1);
}
}
else
{
/* Default action if we haven't recognized something
and returned earlier. */
CC_STATUS_INIT;
}
 
if (cc_status.value2 != 0)
switch (GET_CODE (cc_status.value2))
{
/* These logical operations can generate several insns.
The flags are setup according to what is generated. */
case IOR:
case XOR:
case AND:
break;
 
/* The (not ...) generates several 'com' instructions for
non QImode. We have to invalidate the flags. */
case NOT:
if (GET_MODE (cc_status.value2) != QImode)
CC_STATUS_INIT;
break;
 
case PLUS:
case MINUS:
case MULT:
case DIV:
case UDIV:
case MOD:
case UMOD:
case NEG:
if (GET_MODE (cc_status.value2) != VOIDmode)
cc_status.flags |= CC_NO_OVERFLOW;
break;
 
/* The asl sets the overflow bit in such a way that this
makes the flags unusable for a next compare insn. */
case ASHIFT:
case ROTATE:
case ROTATERT:
if (GET_MODE (cc_status.value2) != VOIDmode)
cc_status.flags |= CC_NO_OVERFLOW;
break;
 
/* A load/store instruction does not affect the carry. */
case MEM:
case SYMBOL_REF:
case REG:
case CONST_INT:
cc_status.flags |= CC_NO_OVERFLOW;
break;
 
default:
break;
}
if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
&& cc_status.value2
&& reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
cc_status.value2 = 0;
 
else if (cc_status.value1 && side_effects_p (cc_status.value1))
cc_status.value1 = 0;
 
else if (cc_status.value2 && side_effects_p (cc_status.value2))
cc_status.value2 = 0;
}
 
/* The current instruction does not affect the flags but changes
the register 'reg'. See if the previous flags can be kept for the
next instruction to avoid a comparison. */
void
m68hc11_notice_keep_cc (rtx reg)
{
if (reg == 0
|| cc_prev_status.value1 == 0
|| rtx_equal_p (reg, cc_prev_status.value1)
|| (cc_prev_status.value2
&& reg_mentioned_p (reg, cc_prev_status.value2)))
CC_STATUS_INIT;
else
cc_status = cc_prev_status;
}
 
 
/* Machine Specific Reorg. */
 
/* Z register replacement:
 
GCC treats the Z register as an index base address register like
X or Y. In general, it uses it during reload to compute the address
of some operand. This helps the reload pass to avoid to fall into the
register spill failure.
 
The Z register is in the A_REGS class. In the machine description,
the 'A' constraint matches it. The 'x' or 'y' constraints do not.
 
It can appear everywhere an X or Y register can appear, except for
some templates in the clobber section (when a clobber of X or Y is asked).
For a given instruction, the template must ensure that no more than
2 'A' registers are used. Otherwise, the register replacement is not
possible.
 
To replace the Z register, the algorithm is not terrific:
1. Insns that do not use the Z register are not changed
2. When a Z register is used, we scan forward the insns to see
a potential register to use: either X or Y and sometimes D.
We stop when a call, a label or a branch is seen, or when we
detect that both X and Y are used (probably at different times, but it does
not matter).
3. The register that will be used for the replacement of Z is saved
in a .page0 register or on the stack. If the first instruction that
used Z, uses Z as an input, the value is loaded from another .page0
register. The replacement register is pushed on the stack in the
rare cases where a compare insn uses Z and we couldn't find if X/Y
are dead.
4. The Z register is replaced in all instructions until we reach
the end of the Z-block, as detected by step 2.
5. If we detect that Z is still alive, its value is saved.
If the replacement register is alive, its old value is loaded.
 
The Z register can be disabled with -ffixed-z.
*/
 
struct replace_info
{
rtx first;
rtx replace_reg;
int need_save_z;
int must_load_z;
int must_save_reg;
int must_restore_reg;
rtx last;
int regno;
int x_used;
int y_used;
int can_use_d;
int found_call;
int z_died;
int z_set_count;
rtx z_value;
int must_push_reg;
int save_before_last;
int z_loaded_with_sp;
};
 
static int m68hc11_check_z_replacement (rtx, struct replace_info *);
static void m68hc11_find_z_replacement (rtx, struct replace_info *);
static void m68hc11_z_replacement (rtx);
static void m68hc11_reassign_regs (rtx);
 
int z_replacement_completed = 0;
 
/* Analyze the insn to find out which replacement register to use and
the boundaries of the replacement.
Returns 0 if we reached the last insn to be replaced, 1 if we can
continue replacement in next insns. */
 
static int
m68hc11_check_z_replacement (rtx insn, struct replace_info *info)
{
int this_insn_uses_ix;
int this_insn_uses_iy;
int this_insn_uses_z;
int this_insn_uses_z_in_dst;
int this_insn_uses_d;
rtx body;
int z_dies_here;
 
/* A call is said to clobber the Z register, we don't need
to save the value of Z. We also don't need to restore
the replacement register (unless it is used by the call). */
if (GET_CODE (insn) == CALL_INSN)
{
body = PATTERN (insn);
 
info->can_use_d = 0;
 
/* If the call is an indirect call with Z, we have to use the
Y register because X can be used as an input (D+X).
We also must not save Z nor restore Y. */
if (reg_mentioned_p (z_reg, body))
{
insn = NEXT_INSN (insn);
info->x_used = 1;
info->y_used = 0;
info->found_call = 1;
info->must_restore_reg = 0;
info->last = NEXT_INSN (insn);
}
info->need_save_z = 0;
return 0;
}
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
return 0;
 
if (GET_CODE (insn) == JUMP_INSN)
{
if (reg_mentioned_p (z_reg, insn) == 0)
return 0;
 
info->can_use_d = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->need_save_z = 0;
info->last = NEXT_INSN (insn);
return 0;
}
if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
{
return 1;
}
 
/* Z register dies here. */
z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
 
body = PATTERN (insn);
if (GET_CODE (body) == SET)
{
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
 
/* Condition code is set here. We have to restore the X/Y and
save into Z before any test/compare insn because once we save/restore
we can change the condition codes. When the compare insn uses Z and
we can't use X/Y, the comparison is made with the *ZREG soft register
(this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
if (dst == cc0_rtx)
{
if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
|| (GET_CODE (src) == COMPARE &&
((rtx_equal_p (XEXP (src, 0), z_reg)
&& H_REG_P (XEXP (src, 1)))
|| (rtx_equal_p (XEXP (src, 1), z_reg)
&& H_REG_P (XEXP (src, 0))))))
{
if (insn == info->first)
{
info->must_load_z = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->need_save_z = 0;
info->found_call = 1;
info->regno = SOFT_Z_REGNUM;
info->last = NEXT_INSN (insn);
}
return 0;
}
if (reg_mentioned_p (z_reg, src) == 0)
{
info->can_use_d = 0;
return 0;
}
 
if (insn != info->first)
return 0;
 
/* Compare insn which uses Z. We have to save/restore the X/Y
register without modifying the condition codes. For this
we have to use a push/pop insn. */
info->must_push_reg = 1;
info->last = insn;
}
 
/* Z reg is set to something new. We don't need to load it. */
if (Z_REG_P (dst))
{
if (!reg_mentioned_p (z_reg, src))
{
/* Z reg is used before being set. Treat this as
a new sequence of Z register replacement. */
if (insn != info->first)
{
return 0;
}
info->must_load_z = 0;
}
info->z_set_count++;
info->z_value = src;
if (SP_REG_P (src))
info->z_loaded_with_sp = 1;
}
else if (reg_mentioned_p (z_reg, dst))
info->can_use_d = 0;
 
this_insn_uses_d = reg_mentioned_p (d_reg, src)
| reg_mentioned_p (d_reg, dst);
this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
| reg_mentioned_p (ix_reg, dst);
this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
| reg_mentioned_p (iy_reg, dst);
this_insn_uses_z = reg_mentioned_p (z_reg, src);
 
/* If z is used as an address operand (like (MEM (reg z))),
we can't replace it with d. */
if (this_insn_uses_z && !Z_REG_P (src)
&& !(m68hc11_arith_operator (src, GET_MODE (src))
&& Z_REG_P (XEXP (src, 0))
&& !reg_mentioned_p (z_reg, XEXP (src, 1))
&& insn == info->first
&& dead_register_here (insn, d_reg)))
info->can_use_d = 0;
 
this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
if (TARGET_M6812 && !z_dies_here
&& ((this_insn_uses_z && side_effects_p (src))
|| (this_insn_uses_z_in_dst && side_effects_p (dst))))
{
info->need_save_z = 1;
info->z_set_count++;
}
this_insn_uses_z |= this_insn_uses_z_in_dst;
 
if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
{
fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
}
 
if (this_insn_uses_d)
info->can_use_d = 0;
 
/* IX and IY are used at the same time, we have to restore
the value of the scratch register before this insn. */
if (this_insn_uses_ix && this_insn_uses_iy)
{
return 0;
}
 
if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
info->can_use_d = 0;
 
if (info->x_used == 0 && this_insn_uses_ix)
{
if (info->y_used)
{
/* We have a (set (REG:HI X) (REG:HI Z)).
Since we use Z as the replacement register, this insn
is no longer necessary. We turn it into a note. We must
not reload the old value of X. */
if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
{
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
}
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->found_call = 1;
info->can_use_d = 0;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
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;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
info->last = NEXT_INSN (insn);
return 0;
}
 
if (Y_REG_P (dst)
&& (rtx_equal_p (src, z_reg)
|| (z_dies_here && !reg_mentioned_p (iy_reg, src))))
{
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
info->last = NEXT_INSN (insn);
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
&& !reg_mentioned_p (iy_reg, src))
{
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
else if (TARGET_M6812 && side_effects_p (src))
{
info->last = 0;
info->must_restore_reg = 0;
return 0;
}
else
{
info->save_before_last = 1;
}
info->must_restore_reg = 0;
info->last = NEXT_INSN (insn);
}
else if (info->can_use_d)
{
info->last = NEXT_INSN (insn);
info->y_used = 1;
}
 
return 0;
}
info->y_used = 1;
if (z_dies_here && !reg_mentioned_p (iy_reg, src)
&& GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
{
info->need_save_z = 0;
info->z_died = 1;
info->last = NEXT_INSN (insn);
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
return 0;
}
if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
{
info->regno = HARD_Y_REGNUM;
info->must_restore_reg = 0;
info->must_save_reg = 0;
return 0;
}
}
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
if (info->last == 0)
info->last = NEXT_INSN (insn);
return 0;
}
return info->last != NULL_RTX ? 0 : 1;
}
if (GET_CODE (body) == PARALLEL)
{
int i;
char ix_clobber = 0;
char iy_clobber = 0;
char z_clobber = 0;
this_insn_uses_iy = 0;
this_insn_uses_ix = 0;
this_insn_uses_z = 0;
 
for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
{
rtx x;
int uses_ix, uses_iy, uses_z;
 
x = XVECEXP (body, 0, i);
 
if (info->can_use_d && reg_mentioned_p (d_reg, x))
info->can_use_d = 0;
 
uses_ix = reg_mentioned_p (ix_reg, x);
uses_iy = reg_mentioned_p (iy_reg, x);
uses_z = reg_mentioned_p (z_reg, x);
if (GET_CODE (x) == CLOBBER)
{
ix_clobber |= uses_ix;
iy_clobber |= uses_iy;
z_clobber |= uses_z;
}
else
{
this_insn_uses_ix |= uses_ix;
this_insn_uses_iy |= uses_iy;
this_insn_uses_z |= uses_z;
}
if (uses_z && GET_CODE (x) == SET)
{
rtx dst = XEXP (x, 0);
 
if (Z_REG_P (dst))
info->z_set_count++;
}
if (TARGET_M6812 && uses_z && side_effects_p (x))
info->need_save_z = 1;
 
if (z_clobber)
info->need_save_z = 0;
}
if (debug_m6811)
{
printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
this_insn_uses_ix, this_insn_uses_iy,
this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
debug_rtx (insn);
}
if (this_insn_uses_z)
info->can_use_d = 0;
 
if (z_clobber && info->first != insn)
{
info->need_save_z = 0;
info->last = insn;
return 0;
}
if (z_clobber && info->x_used == 0 && info->y_used == 0)
{
if (this_insn_uses_z == 0 && insn == info->first)
{
info->must_load_z = 0;
}
if (dead_register_here (insn, d_reg))
{
info->regno = HARD_D_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (dead_register_here (insn, ix_reg))
{
info->regno = HARD_X_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (dead_register_here (insn, iy_reg))
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
if (info->regno >= 0)
{
info->last = NEXT_INSN (insn);
return 0;
}
if (this_insn_uses_ix == 0)
{
info->regno = HARD_X_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
else if (this_insn_uses_iy == 0)
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
else
{
info->regno = HARD_D_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
info->last = NEXT_INSN (insn);
return 0;
}
 
if (((info->x_used || this_insn_uses_ix) && iy_clobber)
|| ((info->y_used || this_insn_uses_iy) && ix_clobber))
{
if (this_insn_uses_z)
{
if (info->y_used == 0 && iy_clobber)
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
if (info->first != insn
&& ((info->y_used && ix_clobber)
|| (info->x_used && iy_clobber)))
info->last = insn;
else
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
}
return 0;
}
if (this_insn_uses_ix && this_insn_uses_iy)
{
if (this_insn_uses_z)
{
fatal_insn ("cannot do z-register replacement", insn);
}
return 0;
}
if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
{
if (info->y_used)
{
return 0;
}
info->x_used = 1;
if (iy_clobber || z_clobber)
{
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
return 0;
}
}
 
if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
{
if (info->x_used)
{
return 0;
}
info->y_used = 1;
if (ix_clobber || z_clobber)
{
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
return 0;
}
}
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
return 1;
}
if (GET_CODE (body) == CLOBBER)
{
 
/* IX and IY are used at the same time, we have to restore
the value of the scratch register before this insn. */
if (this_insn_uses_ix && this_insn_uses_iy)
{
return 0;
}
if (info->x_used == 0 && this_insn_uses_ix)
{
if (info->y_used)
{
return 0;
}
info->x_used = 1;
}
if (info->y_used == 0 && this_insn_uses_iy)
{
if (info->x_used)
{
return 0;
}
info->y_used = 1;
}
return 1;
}
return 1;
}
 
static void
m68hc11_find_z_replacement (rtx insn, struct replace_info *info)
{
int reg;
 
info->replace_reg = NULL_RTX;
info->must_load_z = 1;
info->need_save_z = 1;
info->must_save_reg = 1;
info->must_restore_reg = 1;
info->first = insn;
info->x_used = 0;
info->y_used = 0;
info->can_use_d = TARGET_M6811 ? 1 : 0;
info->found_call = 0;
info->z_died = 0;
info->last = 0;
info->regno = -1;
info->z_set_count = 0;
info->z_value = NULL_RTX;
info->must_push_reg = 0;
info->save_before_last = 0;
info->z_loaded_with_sp = 0;
 
/* Scan the insn forward to find an address register that is not used.
Stop when:
- the flow of the program changes,
- when we detect that both X and Y are necessary,
- when the Z register dies,
- when the condition codes are set. */
 
for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
{
if (m68hc11_check_z_replacement (insn, info) == 0)
break;
}
 
/* May be we can use Y or X if they contain the same value as Z.
This happens very often after the reload. */
if (info->z_set_count == 1)
{
rtx p = info->first;
rtx v = 0;
 
if (info->x_used)
{
v = find_last_value (iy_reg, &p, insn, 1);
}
else if (info->y_used)
{
v = find_last_value (ix_reg, &p, insn, 1);
}
if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
{
if (info->x_used)
info->regno = HARD_Y_REGNUM;
else
info->regno = HARD_X_REGNUM;
info->must_load_z = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->found_call = 1;
}
}
if (info->z_set_count == 0)
info->need_save_z = 0;
 
if (insn == 0)
info->need_save_z = 0;
 
if (info->last == 0)
info->last = insn;
 
if (info->regno >= 0)
{
reg = info->regno;
info->replace_reg = gen_rtx_REG (HImode, reg);
}
else if (info->can_use_d)
{
reg = HARD_D_REGNUM;
info->replace_reg = d_reg;
}
else if (info->x_used)
{
reg = HARD_Y_REGNUM;
info->replace_reg = iy_reg;
}
else
{
reg = HARD_X_REGNUM;
info->replace_reg = ix_reg;
}
info->regno = reg;
 
if (info->must_save_reg && info->must_restore_reg)
{
if (insn && dead_register_here (insn, info->replace_reg))
{
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
}
}
 
/* The insn uses the Z register. Find a replacement register for it
(either X or Y) and replace it in the insn and the next ones until
the flow changes or the replacement register is used. Instructions
are emitted before and after the Z-block to preserve the value of
Z and of the replacement register. */
 
static void
m68hc11_z_replacement (rtx insn)
{
rtx replace_reg_qi;
rtx replace_reg;
struct replace_info info;
 
/* Find trivial case where we only need to replace z with the
equivalent soft register. */
if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
{
rtx body = PATTERN (insn);
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
 
if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
{
XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
return;
}
else if (Z_REG_P (src)
&& ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
{
XEXP (body, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
return;
}
else if (D_REG_P (dst)
&& m68hc11_arith_operator (src, GET_MODE (src))
&& D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
{
XEXP (src, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
return;
}
else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
&& INTVAL (src) == 0)
{
XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
/* Force it to be re-recognized. */
INSN_CODE (insn) = -1;
return;
}
}
 
m68hc11_find_z_replacement (insn, &info);
 
replace_reg = info.replace_reg;
replace_reg_qi = NULL_RTX;
 
/* Save the X register in a .page0 location. */
if (info.must_save_reg && !info.must_push_reg)
{
rtx dst;
 
if (info.must_push_reg && 0)
dst = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
 
emit_insn_before (gen_movhi (dst,
gen_rtx_REG (HImode, info.regno)), insn);
}
if (info.must_load_z && !info.must_push_reg)
{
emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
gen_rtx_REG (HImode, SOFT_Z_REGNUM)),
insn);
}
 
 
/* Replace all occurrence of Z by replace_reg.
Stop when the last instruction to replace is reached.
Also stop when we detect a change in the flow (but it's not
necessary; just safeguard). */
 
for (; insn && insn != info.last; insn = NEXT_INSN (insn))
{
rtx body;
 
if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
break;
 
if (GET_CODE (insn) != INSN
&& GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
continue;
 
body = PATTERN (insn);
if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
|| GET_CODE (body) == ASM_OPERANDS
|| GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
rtx note;
 
if (debug_m6811 && reg_mentioned_p (replace_reg, body))
{
printf ("Reg mentioned here...:\n");
fflush (stdout);
debug_rtx (insn);
}
 
/* Stack pointer was decremented by 2 due to the push.
Correct that by adding 2 to the destination. */
if (info.must_push_reg
&& info.z_loaded_with_sp && GET_CODE (body) == SET)
{
rtx src, dst;
 
src = SET_SRC (body);
dst = SET_DEST (body);
if (SP_REG_P (src) && Z_REG_P (dst))
emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
}
 
/* Replace any (REG:HI Z) occurrence by either X or Y. */
if (!validate_replace_rtx (z_reg, replace_reg, insn))
{
INSN_CODE (insn) = -1;
if (!validate_replace_rtx (z_reg, replace_reg, insn))
fatal_insn ("cannot do z-register replacement", insn);
}
 
/* Likewise for (REG:QI Z). */
if (reg_mentioned_p (z_reg, insn))
{
if (replace_reg_qi == NULL_RTX)
replace_reg_qi = gen_rtx_REG (QImode, REGNO (replace_reg));
validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
}
 
/* If there is a REG_INC note on Z, replace it with a
REG_INC note on the replacement register. This is necessary
to make sure that the flow pass will identify the change
and it will not remove a possible insn that saves Z. */
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_INC
&& GET_CODE (XEXP (note, 0)) == REG
&& REGNO (XEXP (note, 0)) == REGNO (z_reg))
{
XEXP (note, 0) = replace_reg;
}
}
}
if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
break;
}
 
/* Save Z before restoring the old value. */
if (insn && info.need_save_z && !info.must_push_reg)
{
rtx save_pos_insn = insn;
 
/* If Z is clobber by the last insn, we have to save its value
before the last instruction. */
if (info.save_before_last)
save_pos_insn = PREV_INSN (save_pos_insn);
 
emit_insn_before (gen_movhi (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
gen_rtx_REG (HImode, info.regno)),
save_pos_insn);
}
 
if (info.must_push_reg && info.last)
{
rtx new_body, body;
 
body = PATTERN (info.last);
new_body = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (3, body,
gen_rtx_USE (VOIDmode,
replace_reg),
gen_rtx_USE (VOIDmode,
gen_rtx_REG (HImode,
SOFT_Z_REGNUM))));
PATTERN (info.last) = new_body;
 
/* Force recognition on insn since we changed it. */
INSN_CODE (insn) = -1;
 
if (!validate_replace_rtx (z_reg, replace_reg, info.last))
{
fatal_insn ("invalid Z register replacement for insn", insn);
}
insn = NEXT_INSN (info.last);
}
 
/* Restore replacement register unless it was died. */
if (insn && info.must_restore_reg && !info.must_push_reg)
{
rtx dst;
 
if (info.must_push_reg && 0)
dst = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
 
emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
dst), insn);
}
 
}
 
 
/* Scan all the insn and re-affects some registers
- The Z register (if it was used), is affected to X or Y depending
on the instruction. */
 
static void
m68hc11_reassign_regs (rtx first)
{
rtx insn;
 
ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
z_reg_qi = gen_rtx_REG (QImode, HARD_Z_REGNUM);
 
/* Scan all insns to replace Z by X or Y preserving the old value
of X/Y and restoring it afterward. */
 
for (insn = first; insn; insn = NEXT_INSN (insn))
{
rtx body;
 
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
continue;
 
if (!INSN_P (insn))
continue;
 
body = PATTERN (insn);
if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
continue;
 
if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
|| GET_CODE (body) == ASM_OPERANDS
|| GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
continue;
 
if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
|| GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
 
/* If Z appears in this insn, replace it in the current insn
and the next ones until the flow changes or we have to
restore back the replacement register. */
 
if (reg_mentioned_p (z_reg, body))
{
m68hc11_z_replacement (insn);
}
}
else
{
printf ("insn not handled by Z replacement:\n");
fflush (stdout);
debug_rtx (insn);
}
}
}
 
 
/* Machine-dependent reorg pass.
Specific optimizations are defined here:
- this pass changes the Z register into either X or Y
(it preserves X/Y previous values in a memory slot in page0).
 
When this pass is finished, the global variable
'z_replacement_completed' is set to 2. */
 
static void
m68hc11_reorg (void)
{
int split_done = 0;
rtx insn, 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)
{
/* Before recomputing the REG_DEAD notes, remove all of them.
This is necessary because the reload_cse_regs() pass can
have replaced some (MEM) with a register. In that case,
the REG_DEAD that could exist for that register may become
wrong. */
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
{
rtx *pnote;
 
pnote = &REG_NOTES (insn);
while (*pnote != 0)
{
if (REG_NOTE_KIND (*pnote) == REG_DEAD)
*pnote = XEXP (*pnote, 1);
else
pnote = &XEXP (*pnote, 1);
}
}
}
 
life_analysis (PROP_REG_INFO | PROP_DEATH_NOTES);
}
 
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)))
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
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 class,
int in ATTRIBUTE_UNUSED)
{
if (class <= H_REGS && class > 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)
{
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);
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) + rtx_cost (XEXP (x, 1), code);
total += m68hc11_cost->shift_var;
return total;
 
case AND:
case XOR:
case IOR:
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
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) + rtx_cost (XEXP (x, 1), code);
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) + rtx_cost (XEXP (x, 1), code);
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)
+ rtx_cost (XEXP (XEXP (x, 1), 0), code);
 
/* 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)
+ rtx_cost (XEXP (XEXP (x, 1), 0), code);
 
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
switch (mode)
{
case QImode:
total += m68hc11_cost->multQI;
break;
 
case HImode:
total += m68hc11_cost->multHI;
break;
 
case SImode:
default:
total += m68hc11_cost->multSI;
break;
}
return total;
 
case NEG:
case SIGN_EXTEND:
extra_cost = COSTS_N_INSNS (2);
 
/* Fall through */
case NOT:
case COMPARE:
case ABS:
case ZERO_EXTEND:
total = extra_cost + rtx_cost (XEXP (x, 0), code);
if (mode == QImode)
{
return total + COSTS_N_INSNS (1);
}
if (mode == HImode)
{
return total + COSTS_N_INSNS (2);
}
if (mode == SImode)
{
return total + COSTS_N_INSNS (4);
}
return total + COSTS_N_INSNS (8);
 
case IF_THEN_ELSE:
if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
return COSTS_N_INSNS (1);
 
return COSTS_N_INSNS (1);
 
default:
return COSTS_N_INSNS (4);
}
}
 
static bool
m68hc11_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
/* Constants are cheap. Moving them in registers must be avoided
because most instructions do not handle two register operands. */
case CONST_INT:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
/* Logical and arithmetic operations with a constant operand are
better because they are not supported with two registers. */
/* 'clr' is slow */
if (outer_code == SET && x == const0_rtx)
/* After reload, the reload_cse pass checks the cost to change
a SET into a PLUS. Make const0 cheap then. */
*total = 1 - reload_completed;
else
*total = 0;
return true;
case ROTATE:
case ROTATERT:
case ASHIFT:
case LSHIFTRT:
case ASHIFTRT:
case MINUS:
case PLUS:
case AND:
case XOR:
case IOR:
case UDIV:
case DIV:
case MOD:
case MULT:
case NEG:
case SIGN_EXTEND:
case NOT:
case COMPARE:
case ZERO_EXTEND:
case IF_THEN_ELSE:
*total = m68hc11_rtx_costs_1 (x, code, outer_code);
return true;
 
default:
return false;
}
}
 
/* Worker function for TARGET_ASM_FILE_START. */
 
static void
m68hc11_file_start (void)
{
default_file_start ();
fprintf (asm_out_file, "\t.mode %s\n", TARGET_SHORT ? "mshort" : "mlong");
}
 
 
/* Worker function for TARGET_ASM_CONSTRUCTOR. */
 
static void
m68hc11_asm_out_constructor (rtx symbol, int priority)
{
default_ctor_section_asm_out_constructor (symbol, priority);
fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
}
 
/* Worker function for TARGET_ASM_DESTRUCTOR. */
 
static void
m68hc11_asm_out_destructor (rtx symbol, int priority)
{
default_dtor_section_asm_out_destructor (symbol, priority);
fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
}
 
/* Worker function for TARGET_STRUCT_VALUE_RTX. */
 
static rtx
m68hc11_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, HARD_D_REGNUM);
}
 
/* Return true if type TYPE should be returned in memory.
Blocks and data types largers than 4 bytes cannot be returned
in the register (D + X = 4). */
 
static bool
m68hc11_return_in_memory (tree type, 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"
/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
/m68hc11.h
0,0 → 1,1545
/* Definitions of target machine for GNU compiler.
Motorola 68HC11 and 68HC12.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
Contributed by Stephane Carrez (stcarrez@nerim.fr)
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>.
 
Note:
A first 68HC11 port was made by Otto Lind (otto@coactive.com)
on gcc 2.6.3. I have used it as a starting point for this port.
However, this new port is a complete re-write. Its internal
design is completely different. The generated code is not
compatible with the gcc 2.6.3 port.
 
The gcc 2.6.3 port is available at:
 
ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
 
*/
 
/*****************************************************************************
**
** Controlling the Compilation Driver, `gcc'
**
*****************************************************************************/
 
#undef ENDFILE_SPEC
 
/* Compile and assemble for a 68hc11 unless there is a -m68hc12 option. */
#ifndef ASM_SPEC
#define ASM_SPEC \
"%{m68hc12:-m68hc12}" \
"%{m68hcs12:-m68hcs12}" \
"%{!m68hc12:%{!m68hcs12:-m68hc11}} " \
"%{mshort:-mshort}%{!mshort:-mlong} " \
"%{fshort-double:-mshort-double}%{!fshort-double:-mlong-double}"
#endif
 
/* We need to tell the linker the target elf format. Just pass an
emulation option. This can be overridden by -Wl option of gcc. */
#ifndef LINK_SPEC
#define LINK_SPEC \
"%{m68hc12:-m m68hc12elf}" \
"%{m68hcs12:-m m68hc12elf}" \
"%{!m68hc12:%{!m68hcs12:-m m68hc11elf}} " \
"%{!mnorelax:%{!m68hc12:%{!m68hcs12:-relax}}}"
#endif
 
#ifndef LIB_SPEC
#define LIB_SPEC ""
#endif
 
#ifndef CC1_SPEC
#define CC1_SPEC ""
#endif
 
#ifndef CPP_SPEC
#define CPP_SPEC \
"%{mshort:-D__HAVE_SHORT_INT__ -D__INT__=16}\
%{!mshort:-D__INT__=32}\
%{m68hc12:-Dmc6812 -DMC6812 -Dmc68hc12}\
%{m68hcs12:-Dmc6812 -DMC6812 -Dmc68hcs12}\
%{!m68hc12:%{!m68hcs12:-Dmc6811 -DMC6811 -Dmc68hc11}}\
%{fshort-double:-D__HAVE_SHORT_DOUBLE__}\
%{mlong-calls:-D__USE_RTC__}"
#endif
 
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "crt1%O%s"
 
/* Names to predefine in the preprocessor for this target machine. */
#define TARGET_CPU_CPP_BUILTINS() \
do \
{ \
builtin_define_std ("mc68hc1x"); \
} \
while (0)
 
/* As an embedded target, we have no libc. */
#ifndef inhibit_libc
# define inhibit_libc
#endif
 
/* Forward type declaration for prototypes definitions.
rtx_ptr is equivalent to rtx. Can't use the same name. */
struct rtx_def;
typedef struct rtx_def *rtx_ptr;
 
union tree_node;
typedef union tree_node *tree_ptr;
 
/* We can't declare enum machine_mode forward nor include 'machmode.h' here.
Prototypes defined here will use an int instead. It's better than no
prototype at all. */
typedef int enum_machine_mode;
 
/*****************************************************************************
**
** Run-time Target Specification
**
*****************************************************************************/
 
/* Run-time compilation parameters selecting different hardware subsets. */
 
extern short *reg_renumber; /* def in local_alloc.c */
 
#define TARGET_OP_TIME (optimize && optimize_size == 0)
#define TARGET_RELAX (TARGET_NO_DIRECT_MODE)
 
/* Default target_flags if no switches specified. */
#ifndef TARGET_DEFAULT
# define TARGET_DEFAULT 0
#endif
 
/* Define this macro as a C expression for the initializer of an
array of string to tell the driver program which options are
defaults for this target and thus do not need to be handled
specially when using `MULTILIB_OPTIONS'. */
#ifndef MULTILIB_DEFAULTS
# if TARGET_DEFAULT & MASK_M6811
# define MULTILIB_DEFAULTS { "m68hc11" }
# else
# define MULTILIB_DEFAULTS { "m68hc12" }
# endif
#endif
 
/* Print subsidiary information on the compiler version in use. */
#define TARGET_VERSION fprintf (stderr, " (MC68HC11/MC68HC12/MC68HCS12)")
 
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
`OVERRIDE_OPTIONS' to take account of this. This macro, if
defined, is executed once just after all the command options have
been parsed.
 
Don't use this macro to turn on various extra optimizations for
`-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
 
#define OVERRIDE_OPTIONS m68hc11_override_options ();
 
/* Define cost parameters for a given processor variant. */
struct processor_costs {
const int add; /* cost of an add instruction */
const int logical; /* cost of a logical instruction */
const int shift_var;
const int shiftQI_const[8];
const int shiftHI_const[16];
const int multQI;
const int multHI;
const int multSI;
const int divQI;
const int divHI;
const int divSI;
};
 
/* Costs for the current processor. */
extern const struct processor_costs *m68hc11_cost;
 
/* target machine storage layout */
 
/* Define this if most significant byte of a word is the lowest numbered. */
#define BYTES_BIG_ENDIAN 1
 
/* Define this if most significant bit is lowest numbered
in instructions that operate on numbered bit-fields. */
#define BITS_BIG_ENDIAN 0
 
/* Define this if most significant word of a multiword number is numbered. */
#define WORDS_BIG_ENDIAN 1
 
/* Width of a word, in units (bytes). */
#define UNITS_PER_WORD 2
 
/* Definition of size_t. This is really an unsigned short as the
68hc11 only handles a 64K address space. */
#define SIZE_TYPE "short unsigned int"
 
/* A C expression for a string describing the name of the data type
to use for the result of subtracting two pointers. The typedef
name `ptrdiff_t' is defined using the contents of the string.
The 68hc11 only has a 64K address space. */
#define PTRDIFF_TYPE "short int"
 
/* Allocation boundary (bits) for storing pointers in memory. */
#define POINTER_BOUNDARY 8
 
/* Normal alignment required for function parameters on the stack, in bits.
This can't be less than BITS_PER_WORD */
#define PARM_BOUNDARY (BITS_PER_WORD)
 
/* Boundary (bits) on which stack pointer should be aligned. */
#define STACK_BOUNDARY 8
 
/* Allocation boundary (bits) for the code of a function. */
#define FUNCTION_BOUNDARY 8
 
#define BIGGEST_ALIGNMENT 8
 
/* Alignment of field after `int : 0' in a structure. */
#define EMPTY_FIELD_BOUNDARY 8
 
/* Every structure's size must be a multiple of this. */
#define STRUCTURE_SIZE_BOUNDARY 8
 
/* Define this if instructions will fail to work if given data not
on the nominal alignment. If instructions will merely go slower
in that case, do not define this macro. */
#define STRICT_ALIGNMENT 0
 
/* An integer expression for the size in bits of the largest integer
machine mode that should actually be used. All integer machine modes of
this size or smaller can be used for structures and unions with the
appropriate sizes. */
#define MAX_FIXED_MODE_SIZE 64
/* target machine storage layout */
 
/* Size (bits) of the type "int" on target machine
(If undefined, default is BITS_PER_WORD). */
#define INT_TYPE_SIZE (TARGET_SHORT ? 16 : 32)
 
/* Size (bits) of the type "short" on target machine */
#define SHORT_TYPE_SIZE 16
 
/* Size (bits) of the type "long" on target machine */
#define LONG_TYPE_SIZE 32
 
/* Size (bits) of the type "long long" on target machine */
#define LONG_LONG_TYPE_SIZE 64
 
/* A C expression for the size in bits of the type `float' on the
target machine. If you don't define this, the default is one word.
Don't use default: a word is only 16. */
#define FLOAT_TYPE_SIZE 32
 
/* A C expression for the size in bits of the type double on the target
machine. If you don't define this, the default is two words.
Be IEEE compliant. */
#define DOUBLE_TYPE_SIZE 64
 
#define LONG_DOUBLE_TYPE_SIZE 64
 
/* Define this as 1 if `char' should by default be signed; else as 0. */
#define DEFAULT_SIGNED_CHAR 0
 
/* Define these to avoid dependence on meaning of `int'.
Note that WCHAR_TYPE_SIZE is used in cexp.y,
where TARGET_SHORT is not available. */
#define WCHAR_TYPE "short int"
#define WCHAR_TYPE_SIZE 16
 
/* Standard register usage. */
 
#define HARD_REG_SIZE (UNITS_PER_WORD)
 
/* Assign names to real MC68HC11 registers.
A and B registers are not really used (A+B = D)
X register is first so that GCC allocates X+D for 32-bit integers and
the lowpart of that integer will be D. Having the lower part in D is
better for 32<->16bit conversions and for many arithmetic operations. */
#define HARD_X_REGNUM 0
#define HARD_D_REGNUM 1
#define HARD_Y_REGNUM 2
#define HARD_SP_REGNUM 3
#define HARD_PC_REGNUM 4
#define HARD_A_REGNUM 5
#define HARD_B_REGNUM 6
#define HARD_CCR_REGNUM 7
 
/* The Z register does not really exist in the 68HC11. This a fake register
for GCC. It is treated exactly as an index register (X or Y). It is only
in the A_REGS class, which is the BASE_REG_CLASS for GCC. Defining this
register helps the reload pass of GCC. Otherwise, the reload often dies
with register spill failures.
 
The Z register is replaced by either X or Y during the machine specific
reorg (m68hc11_reorg). It is saved in the SOFT_Z_REGNUM soft-register
when this is necessary.
 
It's possible to tell GCC not to use this register with -ffixed-z. */
#define HARD_Z_REGNUM 8
 
/* The frame pointer is a soft-register. It's treated as such by GCC:
it is not and must not be part of the BASE_REG_CLASS. */
#define DEFAULT_HARD_FP_REGNUM (9)
#define HARD_FP_REGNUM (9)
#define HARD_AP_REGNUM (HARD_FP_REGNUM)
 
/* Temporary soft-register used in some cases when an operand came
up into a bad register class (D, X, Y, SP) and gcc failed to
recognize this. This register is never allocated by GCC. */
#define SOFT_TMP_REGNUM 10
 
/* The soft-register which is used to save the Z register
(see Z register replacement notes in m68hc11.c). */
#define SOFT_Z_REGNUM 11
 
/* The soft-register which is used to save either X or Y. */
#define SOFT_SAVED_XY_REGNUM 12
 
/* A fake clobber register for 68HC12 patterns. */
#define FAKE_CLOBBER_REGNUM (13)
 
/* Define 32 soft-registers of 16-bit each. By default,
only 12 of them are enabled and can be used by GCC. The
-msoft-reg-count=<n> option allows to control the number of valid
soft-registers. GCC can put 32-bit values in them
by allocating consecutive registers. The first 3 soft-registers
are never allocated by GCC. They are used in case the insn template needs
a temporary register, or for the Z register replacement. */
 
#define MAX_SOFT_REG_COUNT (32)
#define SOFT_REG_FIXED 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1
#define SOFT_REG_USED 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1
#define SOFT_REG_ORDER \
SOFT_REG_FIRST, SOFT_REG_FIRST+1,SOFT_REG_FIRST+2,SOFT_REG_FIRST+3,\
SOFT_REG_FIRST+4, SOFT_REG_FIRST+5,SOFT_REG_FIRST+6,SOFT_REG_FIRST+7,\
SOFT_REG_FIRST+8, SOFT_REG_FIRST+9,SOFT_REG_FIRST+10,SOFT_REG_FIRST+11,\
SOFT_REG_FIRST+12, SOFT_REG_FIRST+13,SOFT_REG_FIRST+14,SOFT_REG_FIRST+15,\
SOFT_REG_FIRST+16, SOFT_REG_FIRST+17,SOFT_REG_FIRST+18,SOFT_REG_FIRST+19,\
SOFT_REG_FIRST+20, SOFT_REG_FIRST+21,SOFT_REG_FIRST+22,SOFT_REG_FIRST+23,\
SOFT_REG_FIRST+24, SOFT_REG_FIRST+25,SOFT_REG_FIRST+26,SOFT_REG_FIRST+27,\
SOFT_REG_FIRST+28, SOFT_REG_FIRST+29,SOFT_REG_FIRST+30,SOFT_REG_FIRST+31
 
#define SOFT_REG_NAMES \
"*_.d1", "*_.d2", "*_.d3", "*_.d4", \
"*_.d5", "*_.d6", "*_.d7", "*_.d8", \
"*_.d9", "*_.d10", "*_.d11", "*_.d12", \
"*_.d13", "*_.d14", "*_.d15", "*_.d16", \
"*_.d17", "*_.d18", "*_.d19", "*_.d20", \
"*_.d21", "*_.d22", "*_.d23", "*_.d24", \
"*_.d25", "*_.d26", "*_.d27", "*_.d28", \
"*_.d29", "*_.d30", "*_.d31", "*_.d32"
 
/* First available soft-register for GCC. */
#define SOFT_REG_FIRST (SOFT_SAVED_XY_REGNUM+2)
 
/* Last available soft-register for GCC. */
#define SOFT_REG_LAST (SOFT_REG_FIRST+MAX_SOFT_REG_COUNT)
#define SOFT_FP_REGNUM (SOFT_REG_LAST)
#define SOFT_AP_REGNUM (SOFT_FP_REGNUM+1)
 
/* Number of actual hardware registers. The hardware registers are assigned
numbers for the compiler from 0 to just below FIRST_PSEUDO_REGISTER.
All registers that the compiler knows about must be given numbers, even
those that are not normally considered general registers. */
#define FIRST_PSEUDO_REGISTER (SOFT_REG_LAST+2)
 
/* 1 for registers that have pervasive standard uses and are not available
for the register allocator. */
#define FIXED_REGISTERS \
{0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1,1, 1, SOFT_REG_FIXED, 1, 1}
/* X, D, Y, SP,PC,A, B, CCR, Z, FP,ZTMP,ZR,XYR, FK, D1 - D32, SOFT-FP, AP */
 
/* 1 for registers not available across function calls. For our pseudo
registers, all are available. */
#define CALL_USED_REGISTERS \
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, SOFT_REG_USED, 1, 1}
/* X, D, Y, SP,PC,A, B, CCR, Z, FP, ZTMP,ZR,XYR, D1 - 32, SOFT-FP, AP */
 
 
/* Define this macro to change register usage conditional on target flags.
 
The soft-registers are disabled or enabled according to the
-msoft-reg-count=<n> option. */
 
 
#define CONDITIONAL_REGISTER_USAGE (m68hc11_conditional_register_usage ())
 
/* List the order in which to allocate registers. Each register must be
listed once, even those in FIXED_REGISTERS. */
#define REG_ALLOC_ORDER \
{ HARD_D_REGNUM, HARD_X_REGNUM, HARD_Y_REGNUM, \
SOFT_REG_ORDER, HARD_Z_REGNUM, HARD_PC_REGNUM, HARD_A_REGNUM, \
HARD_B_REGNUM, HARD_CCR_REGNUM, HARD_FP_REGNUM, SOFT_FP_REGNUM, \
HARD_SP_REGNUM, SOFT_TMP_REGNUM, SOFT_Z_REGNUM, SOFT_SAVED_XY_REGNUM, \
SOFT_AP_REGNUM, FAKE_CLOBBER_REGNUM }
 
/* A C expression for the number of consecutive hard registers,
starting at register number REGNO, required to hold a value of
mode MODE. */
#define HARD_REGNO_NREGS(REGNO, MODE) \
((Q_REGNO_P (REGNO)) ? (GET_MODE_SIZE (MODE)) : \
((GET_MODE_SIZE (MODE) + HARD_REG_SIZE - 1) / HARD_REG_SIZE))
 
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
- 8 bit values are stored anywhere (except the SP register).
- 16 bit values can be stored in any register whose mode is 16
- 32 bit values can be stored in D, X registers or in a soft register
(except the last one because we need 2 soft registers)
- Values whose size is > 32 bit are not stored in real hard
registers. They may be stored in soft registers if there are
enough of them. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
hard_regno_mode_ok (REGNO,MODE)
 
/* Value is 1 if it is a good idea to tie two pseudo registers when one has
mode MODE1 and one has mode MODE2. If HARD_REGNO_MODE_OK could produce
different values for MODE1 and MODE2, for any hard reg, then this must be
0 for correct output.
 
All modes are tieable except QImode. */
#define MODES_TIEABLE_P(MODE1, MODE2) \
(((MODE1) == (MODE2)) \
|| ((MODE1) != QImode && (MODE2) != QImode))
 
/* Define the classes of registers for register constraints in the
machine description. Also define ranges of constants.
 
One of the classes must always be named ALL_REGS and include all hard regs.
If there is more than one class, another class must be named NO_REGS
and contain no registers.
 
The name GENERAL_REGS must be the name of a class (or an alias for
another name such as ALL_REGS). This is the class of registers
that is allowed by "g" or "r" in a register constraint.
Also, registers outside this class are allocated only when
instructions express preferences for them.
 
The classes must be numbered in nondecreasing order; that is,
a larger-numbered class must never be contained completely
in a smaller-numbered class.
 
For any two classes, it is very desirable that there be another
class that represents their union. */
 
/* The M68hc11 has so few registers that it's not possible for GCC to
do any register allocation without breaking. We extend the processor
registers by having soft registers. These registers are treated as
hard registers by GCC but they are located in memory and accessed by page0
accesses (IND mode). */
enum reg_class
{
NO_REGS,
D_REGS, /* 16-bit data register */
X_REGS, /* 16-bit X register */
Y_REGS, /* 16-bit Y register */
SP_REGS, /* 16 bit stack pointer */
DA_REGS, /* 8-bit A reg. */
DB_REGS, /* 8-bit B reg. */
Z_REGS, /* 16-bit fake Z register */
D8_REGS, /* 8-bit A or B reg. */
Q_REGS, /* 8-bit (byte (QI)) data (A, B or D) */
D_OR_X_REGS, /* D or X register */
D_OR_Y_REGS, /* D or Y register */
D_OR_SP_REGS, /* D or SP register */
X_OR_Y_REGS, /* IX or Y register */
A_REGS, /* 16-bit address register (X, Y, Z) */
X_OR_SP_REGS, /* X or SP register */
Y_OR_SP_REGS, /* Y or SP register */
X_OR_Y_OR_D_REGS, /* X, Y or D */
A_OR_D_REGS, /* X, Y, Z or D */
A_OR_SP_REGS, /* X, Y, Z or SP */
H_REGS, /* 16-bit hard register (D, X, Y, Z, SP) */
S_REGS, /* 16-bit soft register */
D_OR_S_REGS, /* 16-bit soft register or D register */
X_OR_S_REGS, /* 16-bit soft register or X register */
Y_OR_S_REGS, /* 16-bit soft register or Y register */
Z_OR_S_REGS, /* 16-bit soft register or Z register */
SP_OR_S_REGS, /* 16-bit soft register or SP register */
D_OR_X_OR_S_REGS, /* 16-bit soft register or D or X register */
D_OR_Y_OR_S_REGS, /* 16-bit soft register or D or Y register */
D_OR_SP_OR_S_REGS, /* 16-bit soft register or D or SP register */
A_OR_S_REGS, /* 16-bit soft register or X, Y registers */
D_OR_A_OR_S_REGS, /* 16-bit soft register or D, X, Y registers */
TMP_REGS, /* 16 bit fake scratch register */
D_OR_A_OR_TMP_REGS, /* General scratch register */
G_REGS, /* 16-bit general register
(H_REGS + soft registers) */
ALL_REGS,
LIM_REG_CLASSES
};
 
/* alias GENERAL_REGS to G_REGS. */
#define GENERAL_REGS G_REGS
 
#define N_REG_CLASSES (int) LIM_REG_CLASSES
 
/* Give names of register classes as strings for dump file. */
#define REG_CLASS_NAMES \
{ "NO_REGS", \
"D_REGS", \
"X_REGS", \
"Y_REGS", \
"SP_REGS", \
"DA_REGS", \
"DB_REGS", \
"D8_REGS", \
"Z_REGS", \
"Q_REGS", \
"D_OR_X_REGS", \
"D_OR_Y_REGS", \
"D_OR_SP_REGS", \
"X_OR_Y_REGS", \
"A_REGS", \
"X_OR_SP_REGS", \
"Y_OR_SP_REGS", \
"X_OR_Y_OR_D_REGS", \
"A_OR_D_REGS", \
"A_OR_SP_REGS", \
"H_REGS", \
"S_REGS", \
"D_OR_S_REGS", \
"X_OR_S_REGS", \
"Y_OR_S_REGS", \
"Z_OR_S_REGS", \
"SP_OR_S_REGS", \
"D_OR_X_OR_S_REGS", \
"D_OR_Y_OR_S_REGS", \
"D_OR_SP_OR_S_REGS", \
"A_OR_S_REGS", \
"D_OR_A_OR_S_REGS", \
"TMP_REGS", \
"D_OR_A_OR_TMP_REGS", \
"G_REGS", \
"ALL_REGS" }
 
/* An initializer containing the contents of the register classes,
as integers which are bit masks. The Nth integer specifies the
contents of class N. The way the integer MASK is interpreted is
that register R is in the class if `MASK & (1 << R)' is 1. */
 
/*--------------------------------------------------------------
X 0x00000001
D 0x00000002
Y 0x00000004
SP 0x00000008
PC 0x00000010
A 0x00000020
B 0x00000040
CCR 0x00000080
Z 0x00000100
FRAME 0x00000200
ZTMP 0x00000400
ZREG 0x00000800
XYREG 0x00001000
FAKE 0x00002000
Di 0xFFFFc000, 0x03FFF
SFRAME 0x00000000, 0x04000
AP 0x00000000, 0x08000
 
D_OR_X_REGS represents D+X. It is used for 32-bits numbers.
A_REGS represents a valid base register for indexing. It represents
X,Y and the Z register.
S_REGS represents the soft-registers. This includes the hard frame
and soft frame registers.
--------------------------------------------------------------*/
 
#define REG_CLASS_CONTENTS \
/* NO_REGS */ {{ 0x00000000, 0x00000000 }, \
/* D_REGS */ { 0x00000002, 0x00000000 }, /* D */ \
/* X_REGS */ { 0x00000001, 0x00000000 }, /* X */ \
/* Y_REGS */ { 0x00000004, 0x00000000 }, /* Y */ \
/* SP_REGS */ { 0x00000008, 0x00000000 }, /* SP */ \
/* DA_REGS */ { 0x00000020, 0x00000000 }, /* A */ \
/* DB_REGS */ { 0x00000040, 0x00000000 }, /* B */ \
/* Z_REGS */ { 0x00000100, 0x00000000 }, /* Z */ \
/* D8_REGS */ { 0x00000060, 0x00000000 }, /* A B */ \
/* Q_REGS */ { 0x00000062, 0x00000000 }, /* A B D */ \
/* D_OR_X_REGS */ { 0x00000003, 0x00000000 }, /* D X */ \
/* D_OR_Y_REGS */ { 0x00000006, 0x00000000 }, /* D Y */ \
/* D_OR_SP_REGS */ { 0x0000000A, 0x00000000 }, /* D SP */ \
/* X_OR_Y_REGS */ { 0x00000005, 0x00000000 }, /* X Y */ \
/* A_REGS */ { 0x00000105, 0x00000000 }, /* X Y Z */ \
/* X_OR_SP_REGS */ { 0x00000009, 0x00000000 }, /* X SP */ \
/* Y_OR_SP_REGS */ { 0x0000000C, 0x00000000 }, /* Y SP */ \
/* X_OR_Y_OR_D_REGS */ { 0x00000007, 0x00000000 }, /* D X Y */ \
/* A_OR_D_REGS */ { 0x00000107, 0x00000000 }, /* D X Y Z */ \
/* A_OR_SP_REGS */ { 0x0000010D, 0x00000000 }, /* X Y SP */ \
/* H_REGS */ { 0x0000010F, 0x00000000 }, /* D X Y SP */ \
/* S_REGS */ { 0xFFFFDE00, 0x00007FFF }, /* _.D,..,FP,Z* */ \
/* D_OR_S_REGS */ { 0xFFFFDE02, 0x00007FFF }, /* D _.D */ \
/* X_OR_S_REGS */ { 0xFFFFDE01, 0x00007FFF }, /* X _.D */ \
/* Y_OR_S_REGS */ { 0xFFFFDE04, 0x00007FFF }, /* Y _.D */ \
/* Z_OR_S_REGS */ { 0xFFFFDF00, 0x00007FFF }, /* Z _.D */ \
/* SP_OR_S_REGS */ { 0xFFFFDE08, 0x00007FFF }, /* SP _.D */ \
/* D_OR_X_OR_S_REGS */ { 0xFFFFDE03, 0x00007FFF }, /* D X _.D */ \
/* D_OR_Y_OR_S_REGS */ { 0xFFFFDE06, 0x00007FFF }, /* D Y _.D */ \
/* D_OR_SP_OR_S_REGS */ { 0xFFFFDE0A, 0x00007FFF }, /* D SP _.D */ \
/* A_OR_S_REGS */ { 0xFFFFDF05, 0x00007FFF }, /* X Y _.D */ \
/* D_OR_A_OR_S_REGS */ { 0xFFFFDF07, 0x00007FFF }, /* D X Y _.D */ \
/* TMP_REGS */ { 0x00002000, 0x00000000 }, /* FAKE */ \
/* D_OR_A_OR_TMP_REGS*/ { 0x00002107, 0x00000000 }, /* D X Y Z Fake */ \
/* G_REGS */ { 0xFFFFFF1F, 0x00007FFF }, /* ? _.D D X Y */ \
/* ALL_REGS*/ { 0xFFFFFFFF, 0x00007FFF }}
 
 
/* set up a C expression whose value is a register class containing hard
register REGNO */
#define Q_REGNO_P(REGNO) ((REGNO) == HARD_A_REGNUM \
|| (REGNO) == HARD_B_REGNUM)
#define Q_REG_P(X) (REG_P (X) && Q_REGNO_P (REGNO (X)))
 
#define D_REGNO_P(REGNO) ((REGNO) == HARD_D_REGNUM)
#define D_REG_P(X) (REG_P (X) && D_REGNO_P (REGNO (X)))
 
#define DB_REGNO_P(REGNO) ((REGNO) == HARD_B_REGNUM)
#define DB_REG_P(X) (REG_P (X) && DB_REGNO_P (REGNO (X)))
#define DA_REGNO_P(REGNO) ((REGNO) == HARD_A_REGNUM)
#define DA_REG_P(X) (REG_P (X) && DA_REGNO_P (REGNO (X)))
 
#define X_REGNO_P(REGNO) ((REGNO) == HARD_X_REGNUM)
#define X_REG_P(X) (REG_P (X) && X_REGNO_P (REGNO (X)))
 
#define Y_REGNO_P(REGNO) ((REGNO) == HARD_Y_REGNUM)
#define Y_REG_P(X) (REG_P (X) && Y_REGNO_P (REGNO (X)))
 
#define Z_REGNO_P(REGNO) ((REGNO) == HARD_Z_REGNUM)
#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X)))
 
#define SP_REGNO_P(REGNO) ((REGNO) == HARD_SP_REGNUM)
#define SP_REG_P(X) (REG_P (X) && SP_REGNO_P (REGNO (X)))
 
/* Address register. */
#define A_REGNO_P(REGNO) ((REGNO) == HARD_X_REGNUM \
|| (REGNO) == HARD_Y_REGNUM \
|| (REGNO) == HARD_Z_REGNUM)
#define A_REG_P(X) (REG_P (X) && A_REGNO_P (REGNO (X)))
 
/* M68hc11 hard registers. */
#define H_REGNO_P(REGNO) (D_REGNO_P (REGNO) || A_REGNO_P (REGNO) \
|| SP_REGNO_P (REGNO) || Q_REGNO_P (REGNO))
#define H_REG_P(X) (REG_P (X) && H_REGNO_P (REGNO (X)))
 
#define FAKE_REGNO_P(REGNO) ((REGNO) == FAKE_CLOBBER_REGNUM)
#define FAKE_REG_P(X) (REG_P (X) && FAKE_REGNO_P (REGNO (X)))
 
/* Soft registers (or register emulation for gcc). The temporary register
used by insn template must be part of the S_REGS class so that it
matches the 'u' constraint. */
#define S_REGNO_P(REGNO) ((REGNO) >= SOFT_TMP_REGNUM \
&& (REGNO) <= SOFT_REG_LAST \
&& (REGNO) != FAKE_CLOBBER_REGNUM)
#define S_REG_P(X) (REG_P (X) && S_REGNO_P (REGNO (X)))
 
#define Z_REGNO_P(REGNO) ((REGNO) == HARD_Z_REGNUM)
#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X)))
 
/* General register. */
#define G_REGNO_P(REGNO) (H_REGNO_P (REGNO) || S_REGNO_P (REGNO) \
|| ((REGNO) == HARD_PC_REGNUM) \
|| ((REGNO) == HARD_FP_REGNUM) \
|| ((REGNO) == SOFT_FP_REGNUM) \
|| ((REGNO) == FAKE_CLOBBER_REGNUM) \
|| ((REGNO) == SOFT_AP_REGNUM))
 
#define G_REG_P(X) (REG_P (X) && G_REGNO_P (REGNO (X)))
 
#define REGNO_REG_CLASS(REGNO) \
(D_REGNO_P (REGNO) ? D_REGS : \
(X_REGNO_P (REGNO) ? X_REGS : \
(Y_REGNO_P (REGNO) ? Y_REGS : \
(SP_REGNO_P (REGNO) ? SP_REGS : \
(Z_REGNO_P (REGNO) ? Z_REGS : \
(H_REGNO_P (REGNO) ? H_REGS : \
(FAKE_REGNO_P (REGNO) ? TMP_REGS : \
(S_REGNO_P (REGNO) ? S_REGS : \
(DA_REGNO_P (REGNO) ? DA_REGS: \
(DB_REGNO_P (REGNO) ? DB_REGS: \
(G_REGNO_P (REGNO) ? G_REGS : ALL_REGS)))))))))))
 
 
/* Get reg_class from a letter in the machine description. */
 
extern enum reg_class m68hc11_tmp_regs_class;
#define REG_CLASS_FROM_LETTER(C) \
((C) == 'a' ? DA_REGS : \
(C) == 'A' ? A_REGS : \
(C) == 'b' ? DB_REGS : \
(C) == 'B' ? X_OR_Y_REGS : \
(C) == 'd' ? D_REGS : \
(C) == 'D' ? D_OR_X_REGS : \
(C) == 'q' ? Q_REGS : \
(C) == 'h' ? H_REGS : \
(C) == 't' ? TMP_REGS : \
(C) == 'u' ? S_REGS : \
(C) == 'v' ? m68hc11_tmp_regs_class : \
(C) == 'w' ? SP_REGS : \
(C) == 'x' ? X_REGS : \
(C) == 'y' ? Y_REGS : \
(C) == 'z' ? Z_REGS : NO_REGS)
 
#define PREFERRED_RELOAD_CLASS(X,CLASS) preferred_reload_class(X,CLASS)
 
#define SMALL_REGISTER_CLASSES 1
 
/* A C expression that is nonzero if hard register number REGNO2 can be
considered for use as a rename register for REGNO1 */
 
#define HARD_REGNO_RENAME_OK(REGNO1,REGNO2) \
m68hc11_hard_regno_rename_ok ((REGNO1), (REGNO2))
 
/* A C expression whose value is nonzero if pseudos that have been
assigned to registers of class CLASS would likely be spilled
because registers of CLASS are needed for spill registers.
 
The default value of this macro returns 1 if CLASS has exactly one
register and zero otherwise. On most machines, this default
should be used. Only define this macro to some other expression
if pseudo allocated by `local-alloc.c' end up in memory because
their hard registers were needed for spill registers. If this
macro returns nonzero for those classes, those pseudos will only
be allocated by `global.c', which knows how to reallocate the
pseudo to another register. If there would not be another
register available for reallocation, you should not change the
definition of this macro since the only effect of such a
definition would be to slow down register allocation. */
 
#define CLASS_LIKELY_SPILLED_P(CLASS) \
(((CLASS) == D_REGS) \
|| ((CLASS) == X_REGS) \
|| ((CLASS) == Y_REGS) \
|| ((CLASS) == A_REGS) \
|| ((CLASS) == SP_REGS) \
|| ((CLASS) == D_OR_X_REGS) \
|| ((CLASS) == D_OR_Y_REGS) \
|| ((CLASS) == X_OR_SP_REGS) \
|| ((CLASS) == Y_OR_SP_REGS) \
|| ((CLASS) == D_OR_SP_REGS))
 
/* Return the maximum number of consecutive registers needed to represent
mode MODE in a register of class CLASS. */
#define CLASS_MAX_NREGS(CLASS, MODE) \
(((CLASS) == DA_REGS || (CLASS) == DB_REGS \
|| (CLASS) == D8_REGS || (CLASS) == Q_REGS) ? GET_MODE_SIZE (MODE) \
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
 
/* The letters I, J, K, L and M in a register constraint string
can be used to stand for particular ranges of immediate operands.
This macro defines what the ranges are.
C is the letter, and VALUE is a constant value.
Return 1 if VALUE is in the range specified by C.
 
`K' is for 0.
`L' is for range -65536 to 65536
`M' is for values whose 16-bit low part is 0
'N' is for +1 or -1.
'O' is for 16 (for rotate using swap).
'P' is for range -8 to 2 (used by addhi_sp)
 
'I', 'J' are not used. */
 
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'K' ? (VALUE) == 0 : \
(C) == 'L' ? ((VALUE) >= -65536 && (VALUE) <= 65535) : \
(C) == 'M' ? ((VALUE) & 0x0ffffL) == 0 : \
(C) == 'N' ? ((VALUE) == 1 || (VALUE) == -1) : \
(C) == 'I' ? ((VALUE) >= -2 && (VALUE) <= 2) : \
(C) == 'O' ? (VALUE) == 16 : \
(C) == 'P' ? ((VALUE) <= 2 && (VALUE) >= -8) : 0)
 
/* Similar, but for floating constants, and defining letters G and H.
 
`G' is for 0.0. */
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'G' ? (GET_MODE_CLASS (GET_MODE (VALUE)) == MODE_FLOAT \
&& VALUE == CONST0_RTX (GET_MODE (VALUE))) : 0)
 
/* 'U' represents certain kind of memory indexed operand for 68HC12.
and any memory operand for 68HC11.
'R' represents indexed addressing mode or access to page0 for 68HC11.
For 68HC12, it represents any memory operand. */
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'U' ? m68hc11_small_indexed_indirect_p (OP, GET_MODE (OP)) \
: (C) == 'Q' ? m68hc11_symbolic_p (OP, GET_MODE (OP)) \
: (C) == 'R' ? m68hc11_indirect_p (OP, GET_MODE (OP)) \
: (C) == 'S' ? (memory_operand (OP, GET_MODE (OP)) \
&& non_push_operand (OP, GET_MODE (OP))) : 0)
 
/* Stack layout; function entry, exit and calling. */
 
/* Define this if pushing a word on the stack
makes the stack pointer a smaller address. */
#define STACK_GROWS_DOWNWARD
 
/* Define this to nonzero if the nominal address of the stack frame
is at the high-address end of the local variables;
that is, each additional local variable allocated
goes at a more negative offset in the frame.
 
Define to 0 for 68HC11, the frame pointer is the bottom
of local variables. */
#define FRAME_GROWS_DOWNWARD 0
 
/* Define this if successive arguments to a function occupy decreasing
addresses in the stack. */
/* #define ARGS_GROW_DOWNWARD */
 
/* Offset within stack frame to start allocating local variables at.
If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
first local allocated. Otherwise, it is the offset to the BEGINNING
of the first local allocated. */
#define STARTING_FRAME_OFFSET 0
 
/* Offset of first parameter from the argument pointer register value. */
 
#define FIRST_PARM_OFFSET(FNDECL) 2
 
/* After the prologue, RA is at 0(AP) in the current frame. */
#define RETURN_ADDR_RTX(COUNT, FRAME) \
((COUNT) == 0 \
? gen_rtx_MEM (Pmode, arg_pointer_rtx) \
: 0)
 
/* Before the prologue, the top of the frame is at 2(sp). */
#define INCOMING_FRAME_SP_OFFSET 2
 
/* Define this if functions should assume that stack space has been
allocated for arguments even when their values are passed in
registers.
The value of this macro is the size, in bytes, of the area reserved for
arguments passed in registers.
This space can either be allocated by the caller or be a part of the
machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
says which. */
/* #define REG_PARM_STACK_SPACE(FNDECL) 2 */
 
/* Define this macro if REG_PARM_STACK_SPACE is defined but stack
parameters don't skip the area specified by REG_PARM_STACK_SPACE.
Normally, when a parameter is not passed in registers, it is placed on
the stack beyond the REG_PARM_STACK_SPACE area. Defining this macro
suppresses this behavior and causes the parameter to be passed on the
stack in its natural location. */
/* #define STACK_PARMS_IN_REG_PARM_AREA */
 
/* Register to use for pushing function arguments. */
#define STACK_POINTER_REGNUM HARD_SP_REGNUM
 
/* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM SOFT_FP_REGNUM
 
#define HARD_FRAME_POINTER_REGNUM HARD_FP_REGNUM
 
/* Base register for access to arguments of the function. */
#define ARG_POINTER_REGNUM SOFT_AP_REGNUM
 
/* Register in which static-chain is passed to a function. */
#define STATIC_CHAIN_REGNUM SOFT_Z_REGNUM
 
/* Definitions for register eliminations.
 
This is an array of structures. Each structure initializes one pair
of eliminable registers. The "from" register number is given first,
followed by "to". Eliminations of the same "from" register are listed
in order of preference.
 
We have two registers that are eliminated on the 6811. The pseudo arg
pointer and pseudo frame pointer registers can always be eliminated;
they are replaced with either the stack or the real frame pointer. */
 
#define ELIMINABLE_REGS \
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
 
/* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms may be
accessed via the stack pointer) in functions that seem suitable.
This is computed in `reload', in reload1.c. */
#define FRAME_POINTER_REQUIRED 0
 
/* Given FROM and TO register numbers, say whether this elimination is allowed.
Frame pointer elimination is automatically handled.
 
All other eliminations are valid. */
 
#define CAN_ELIMINATE(FROM, TO) \
((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
? ! frame_pointer_needed \
: 1)
 
 
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
 
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ OFFSET = m68hc11_initial_elimination_offset (FROM, TO); }
 
/* Passing Function Arguments on the Stack. */
 
/* If we generate an insn to push BYTES bytes, this says how many the
stack pointer really advances by. No rounding or alignment needed
for MC6811. */
#define PUSH_ROUNDING(BYTES) (BYTES)
 
/* Value is 1 if returning from a function call automatically pops the
arguments described by the number-of-args field in the call. FUNTYPE is
the data type of the function (as a tree), or for a library call it is
an identifier node for the subroutine name.
The standard MC6811 call, with arg count word, includes popping the
args as part of the call template. */
#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
/* Passing Arguments in Registers. */
 
/* Define a data type for recording info about an argument list
during the scan of that argument list. This data type should
hold all necessary information about the function itself
and about the args processed so far, enough to enable macros
such as FUNCTION_ARG to determine where the next arg should go. */
 
typedef struct m68hc11_args
{
int words;
int nregs;
} CUMULATIVE_ARGS;
 
/* If defined, a C expression which determines whether, and in which direction,
to pad out an argument with extra space. The value should be of type
`enum direction': either `upward' to pad above the argument,
`downward' to pad below, or `none' to inhibit padding.
 
Structures are stored left shifted in their argument slot. */
#define FUNCTION_ARG_PADDING(MODE, TYPE) \
m68hc11_function_arg_padding ((MODE), (TYPE))
 
#undef PAD_VARARGS_DOWN
#define PAD_VARARGS_DOWN \
(m68hc11_function_arg_padding (TYPE_MODE (type), type) == downward)
 
/* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a
function whose data type is FNTYPE. For a library call, FNTYPE is 0. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
(m68hc11_init_cumulative_args (&CUM, FNTYPE, LIBNAME))
 
/* Update the data in CUM to advance over an argument of mode MODE and data
type TYPE. (TYPE is null for libcalls where that information may not be
available.) */
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
(m68hc11_function_arg_advance (&CUM, MODE, TYPE, NAMED))
 
/* Define where to put the arguments to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
 
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis). */
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
(m68hc11_function_arg (&CUM, MODE, TYPE, NAMED))
 
/* Define the profitability of saving registers around calls.
 
Disable this because the saving instructions generated by
caller-save need a reload and the way it is implemented,
it forbids all spill registers at that point. Enabling
caller saving results in spill failure. */
#define CALLER_SAVE_PROFITABLE(REFS,CALLS) 0
 
/* 1 if N is a possible register number for function argument passing.
D is for 16-bit values, X is for 32-bit (X+D). */
#define FUNCTION_ARG_REGNO_P(N) \
(((N) == HARD_D_REGNUM) || ((N) == HARD_X_REGNUM))
 
/* All return values are in the D or X+D registers:
- 8 and 16-bit values are returned in D.
BLKmode are passed in D as pointer.
- 32-bit values are returned in X + D.
The high part is passed in X and the low part in D.
For GCC, the register number must be HARD_X_REGNUM. */
#define FUNCTION_VALUE(VALTYPE, FUNC) \
gen_rtx_REG (TYPE_MODE (VALTYPE), \
((TYPE_MODE (VALTYPE) == BLKmode \
|| GET_MODE_SIZE (TYPE_MODE (VALTYPE)) <= 2) \
? HARD_D_REGNUM : HARD_X_REGNUM))
 
#define LIBCALL_VALUE(MODE) \
gen_rtx_REG (MODE, \
(((MODE) == BLKmode || GET_MODE_SIZE (MODE) <= 2) \
? HARD_D_REGNUM : HARD_X_REGNUM))
 
/* 1 if N is a possible register number for a function value. */
#define FUNCTION_VALUE_REGNO_P(N) \
((N) == HARD_D_REGNUM || (N) == HARD_X_REGNUM)
 
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in functions
that have frame pointers. No definition is equivalent to always zero. */
#define EXIT_IGNORE_STACK 0
 
/* Generating Code for Profiling. */
 
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry. */
#define FUNCTION_PROFILER(FILE, LABELNO) \
fprintf (FILE, "\tldy\t.LP%d\n\tjsr mcount\n", (LABELNO))
/* Length in units of the trampoline for entering a nested function. */
#define TRAMPOLINE_SIZE (TARGET_M6811 ? 11 : 9)
 
/* A C statement to initialize the variable parts of a trampoline.
ADDR is an RTX for the address of the trampoline; FNADDR is an
RTX for the address of the nested function; STATIC_CHAIN is an
RTX for the static chain value that should be passed to the
function when it is called. */
#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
m68hc11_initialize_trampoline ((TRAMP), (FNADDR), (CXT))
 
/* Addressing modes, and classification of registers for them. */
 
#define ADDR_STRICT 0x01 /* Accept only registers in class A_REGS */
#define ADDR_INCDEC 0x02 /* Post/Pre inc/dec */
#define ADDR_INDEXED 0x04 /* D-reg index */
#define ADDR_OFFSET 0x08
#define ADDR_INDIRECT 0x10 /* Accept (mem (mem ...)) for [n,X] */
#define ADDR_CONST 0x20 /* Accept const and symbol_ref */
 
/* The 68HC12 has all the post/pre increment/decrement modes. */
#define HAVE_POST_INCREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
#define HAVE_PRE_INCREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
#define HAVE_POST_DECREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
#define HAVE_PRE_DECREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
 
/* The class value for base registers. This depends on the target:
A_REGS for 68HC11 and A_OR_SP_REGS for 68HC12. The class value
is stored at init time. */
extern enum reg_class m68hc11_base_reg_class;
#define BASE_REG_CLASS m68hc11_base_reg_class
 
/* The class value for index registers. This is NO_REGS for 68HC11. */
 
extern enum reg_class m68hc11_index_reg_class;
#define INDEX_REG_CLASS m68hc11_index_reg_class
 
/* These assume that REGNO is a hard or pseudo reg number. They give nonzero
only if REGNO is a hard reg of the suitable class or a pseudo reg currently
allocated to a suitable hard reg. Since they use reg_renumber, they are
safe only once reg_renumber has been allocated, which happens in
local-alloc.c. */
 
 
/* Internal macro, return 1 if REGNO is a valid base register. */
#define REG_VALID_P(REGNO) ((REGNO) >= 0)
 
extern unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
#define REG_VALID_FOR_BASE_P(REGNO) \
(REG_VALID_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) \
(REG_VALID_P (REGNO) >= 0 && (REGNO) < FIRST_PSEUDO_REGISTER \
&& m68hc11_reg_valid_for_index[REGNO])
 
/* Internal macro, the nonstrict definition for REGNO_OK_FOR_BASE_P. */
#define REGNO_OK_FOR_BASE_NONSTRICT_P(REGNO) \
((REGNO) >= FIRST_PSEUDO_REGISTER \
|| REG_VALID_FOR_BASE_P (REGNO) \
|| (REGNO) == FRAME_POINTER_REGNUM \
|| (REGNO) == HARD_FRAME_POINTER_REGNUM \
|| (REGNO) == ARG_POINTER_REGNUM \
|| (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
 
/* Internal macro, the nonstrict definition for REGNO_OK_FOR_INDEX_P. */
#define REGNO_OK_FOR_INDEX_NONSTRICT_P(REGNO) \
(TARGET_M6812 \
&& ((REGNO) >= FIRST_PSEUDO_REGISTER \
|| REG_VALID_FOR_INDEX_P (REGNO) \
|| (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
 
/* Internal macro, the strict definition for REGNO_OK_FOR_BASE_P. */
#define REGNO_OK_FOR_BASE_STRICT_P(REGNO) \
((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_BASE_P (REGNO) \
: (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
 
/* Internal macro, the strict definition for REGNO_OK_FOR_INDEX_P. */
#define REGNO_OK_FOR_INDEX_STRICT_P(REGNO) \
(TARGET_M6812 \
&& ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_INDEX_P (REGNO) \
: (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
 
#define REGNO_OK_FOR_BASE_P2(REGNO,STRICT) \
((STRICT) ? (REGNO_OK_FOR_BASE_STRICT_P (REGNO)) \
: (REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO)))
 
#define REGNO_OK_FOR_INDEX_P2(REGNO,STRICT) \
((STRICT) ? (REGNO_OK_FOR_INDEX_STRICT_P (REGNO)) \
: (REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO)))
 
#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_BASE_STRICT_P (REGNO)
#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_INDEX_STRICT_P (REGNO)
 
#define REG_OK_FOR_BASE_STRICT_P(X) REGNO_OK_FOR_BASE_STRICT_P (REGNO (X))
#define REG_OK_FOR_BASE_NONSTRICT_P(X) REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO (X))
#define REG_OK_FOR_INDEX_STRICT_P(X) REGNO_OK_FOR_INDEX_STRICT_P (REGNO (X))
#define REG_OK_FOR_INDEX_NONSTRICT_P(X) REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO (X))
 
/* see PUSH_POP_ADDRESS_P() below for an explanation of this. */
#define IS_STACK_PUSH(operand) \
((GET_CODE (operand) == MEM) \
&& (GET_CODE (XEXP (operand, 0)) == PRE_DEC) \
&& (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
 
#define IS_STACK_POP(operand) \
((GET_CODE (operand) == MEM) \
&& (GET_CODE (XEXP (operand, 0)) == POST_INC) \
&& (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
 
/* 1 if X is an rtx for a constant that is a valid address. */
#define CONSTANT_ADDRESS_P(X) (CONSTANT_P (X))
 
/* Maximum number of registers that can appear in a valid memory address */
#define MAX_REGS_PER_ADDRESS 2
 
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a
valid memory address for an instruction. The MODE argument is the
machine mode for the MEM expression that wants to use this address. */
 
/*--------------------------------------------------------------
Valid addresses are either direct or indirect (MEM) versions
of the following forms:
constant N
register ,X
indexed N,X
--------------------------------------------------------------*/
 
/* The range of index that is allowed by indirect addressing. */
 
#define VALID_MIN_OFFSET m68hc11_min_offset
#define VALID_MAX_OFFSET m68hc11_max_offset
 
/* The offset values which are allowed by the n,x and n,y addressing modes.
Take into account the size of the mode because we may have to add
a mode offset to access the lowest part of the data.
(For example, for an SImode, the last valid offset is 252.) */
#define VALID_CONSTANT_OFFSET_P(X,MODE) \
(((GET_CODE (X) == CONST_INT) && \
((INTVAL (X) >= VALID_MIN_OFFSET) \
&& ((INTVAL (X) <= VALID_MAX_OFFSET \
- (HOST_WIDE_INT) (GET_MODE_SIZE (MODE) + 1))))) \
|| (TARGET_M6812 \
&& ((GET_CODE (X) == SYMBOL_REF) \
|| GET_CODE (X) == LABEL_REF \
|| GET_CODE (X) == CONST)))
 
/* This is included to allow stack push/pop operations. Special hacks in the
md and m6811.c files exist to support this. */
#define PUSH_POP_ADDRESS_P(X) \
(((GET_CODE (X) == PRE_DEC) || (GET_CODE (X) == POST_INC)) \
&& SP_REG_P (XEXP (X, 0)))
 
/* Go to ADDR if X is a valid address. */
#ifndef REG_OK_STRICT
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ \
if (m68hc11_go_if_legitimate_address ((X), (MODE), 0)) goto ADDR; \
}
#else
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ \
if (m68hc11_go_if_legitimate_address ((X), (MODE), 1)) goto ADDR; \
}
#endif
 
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its
validity for a certain class. We have two alternate definitions for each
of them. The usual definition accepts all pseudo regs; the other rejects
them unless they have been allocated suitable hard regs. The symbol
REG_OK_STRICT causes the latter definition to be used.
Most source files want to accept pseudo regs in the hope that they will
get allocated to the class that the insn wants them to be in. Source files
for reload pass need to be strict. After reload, it makes no difference,
since pseudo regs have been eliminated by then. */
 
#ifndef REG_OK_STRICT
/* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P(X)
 
/* Nonzero if X is a hard reg that can be used as an index. */
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P(X)
#else
#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P(X)
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_STRICT_P(X)
#endif
 
 
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
MODE and WIN are passed so that this macro can use
GO_IF_LEGITIMATE_ADDRESS.
It is always safe for this macro to do nothing.
It exists to recognize opportunities to optimize the output. */
 
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
{ rtx operand = (X); \
if (m68hc11_legitimize_address (&operand, (OLDX), (MODE))) \
{ \
(X) = operand; \
GO_IF_LEGITIMATE_ADDRESS (MODE,X,WIN); \
} \
}
 
/* Go to LABEL if ADDR (a legitimate address expression)
has an effect that depends on the machine mode it is used for. */
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \
{ \
if (GET_CODE (ADDR) == PRE_DEC || GET_CODE (ADDR) == POST_DEC \
|| GET_CODE (ADDR) == PRE_INC || GET_CODE (ADDR) == POST_INC) \
goto LABEL; \
}
 
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
 
#define LEGITIMATE_CONSTANT_P(X) 1
 
/* Tell final.c how to eliminate redundant test instructions. */
 
#define NOTICE_UPDATE_CC(EXP, INSN) \
m68hc11_notice_update_cc ((EXP), (INSN))
 
/* Move costs between classes of registers */
#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
(m68hc11_register_move_cost (MODE, CLASS1, CLASS2))
 
/* Move cost between register and memory.
- Move to a 16-bit register is reasonable,
- Move to a soft register can be expensive. */
#define MEMORY_MOVE_COST(MODE,CLASS,IN) \
m68hc11_memory_move_cost ((MODE),(CLASS),(IN))
 
/* A C expression for the cost of a branch instruction. A value of 1
is the default; other values are interpreted relative to that.
 
Pretend branches are cheap because GCC generates sub-optimal code
for the default value. */
#define BRANCH_COST 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 */
#define ASM_OUTPUT_EXTERNAL(FILE,DECL,NAME) \
{fputs ("\t; extern\t", FILE); \
assemble_name (FILE, NAME); \
fputs ("\n", FILE);}
 
/* How to refer to registers in assembler output. This sequence is indexed
by compiler's hard-register-number (see above). */
#define REGISTER_NAMES \
{ "x", "d", "y", "sp", "pc", "a", "b", "ccr", "z", \
"*_.frame", "*_.tmp", "*_.z", "*_.xy", "*fake clobber", \
SOFT_REG_NAMES, "*sframe", "*ap"}
 
/* Print an instruction operand X on file FILE. CODE is the code from the
%-spec for printing this operand. If `%z3' was used to print operand
3, then CODE is 'z'. */
 
#define PRINT_OPERAND(FILE, X, CODE) \
print_operand (FILE, X, CODE)
 
/* Print a memory operand whose address is X, on file FILE. */
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \
print_operand_address (FILE, ADDR)
 
/* This is how to output an insn to push/pop a register on the stack.
It need not be very fast code.
 
Don't define because we don't know how to handle that with
the STATIC_CHAIN_REGNUM (soft register). Saving the static
chain must be made inside FUNCTION_PROFILER. */
 
#undef ASM_OUTPUT_REG_PUSH
#undef ASM_OUTPUT_REG_POP
 
/* This is how to output an element of a case-vector that is relative. */
 
#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
fprintf (FILE, "\t%s\tL%d-L%d\n", integer_asm_op (2, TRUE), VALUE, REL)
 
/* This is how to output an element of a case-vector that is absolute. */
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
fprintf (FILE, "\t%s\t.L%d\n", integer_asm_op (2, TRUE), VALUE)
 
/* This is how to output an assembler line that says to advance the
location counter to a multiple of 2**LOG bytes. */
#define ASM_OUTPUT_ALIGN(FILE,LOG) \
do { \
if ((LOG) > 1) \
fprintf ((FILE), "%s\n", ALIGN_ASM_OP); \
} while (0)
 
/* Assembler Commands for Exception Regions. */
 
/* Default values provided by GCC should be ok. Assuming that DWARF-2
frame unwind info is ok for this platform. */
 
#undef PREFERRED_DEBUGGING_TYPE
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
 
/* For the support of memory banks we need addresses that indicate
the page number. */
#define DWARF2_ADDR_SIZE 4
 
/* SCz 2003-07-08: Don't use as dwarf2 .file/.loc directives because
the linker is doing relaxation and it does not adjust the debug_line
sections when it shrinks the code. This results in invalid addresses
when debugging. This does not bless too much the HC11/HC12 as most
applications are embedded and small, hence a reasonable debug info.
This problem is known for binutils 2.13, 2.14 and mainline. */
#undef HAVE_AS_DWARF2_DEBUG_LINE
 
/* The prefix for local labels. You should be able to define this as
an empty string, or any arbitrary string (such as ".", ".L%", etc)
without having to make any other changes to account for the specific
definition. Note it is a string literal, not interpreted by printf
and friends. */
#define LOCAL_LABEL_PREFIX "."
 
/* The prefix for immediate operands. */
#define IMMEDIATE_PREFIX "#"
#define GLOBAL_ASM_OP "\t.globl\t"
 
/* Miscellaneous Parameters. */
 
/* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */
#define CASE_VECTOR_MODE Pmode
 
/* This flag, if defined, says the same insns that convert to a signed fixnum
also convert validly to an unsigned one. */
#define FIXUNS_TRUNC_LIKE_FIX_TRUNC
 
/* Max number of bytes we can move from memory to memory in one
reasonably fast instruction. */
#define MOVE_MAX 2
 
/* MOVE_RATIO is the number of move instructions that is better than a
block move. Make this small on 6811, since the code size grows very
large with each move. */
#define MOVE_RATIO 3
 
/* Define if shifts truncate the shift count which implies one can omit
a sign-extension or zero-extension of a shift count. */
#define SHIFT_COUNT_TRUNCATED 1
 
/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
is done just by pretending it is already truncated. */
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
 
/* Specify the machine mode that pointers have. After generation of rtl, the
compiler makes no further distinction between pointers and any other
objects of this machine mode. */
#define Pmode HImode
 
/* A function address in a call instruction is a byte address (for indexing
purposes) so give the MEM rtx a byte's mode. */
#define FUNCTION_MODE QImode
 
extern int debug_m6811;
extern int z_replacement_completed;
extern int current_function_interrupt;
extern int current_function_trap;
extern int current_function_far;
 
extern GTY(()) rtx m68hc11_compare_op0;
extern GTY(()) rtx m68hc11_compare_op1;
extern GTY(()) rtx m68hc11_soft_tmp_reg;
extern GTY(()) rtx ix_reg;
extern GTY(()) rtx iy_reg;
extern GTY(()) rtx d_reg;
/m68hc11-crt0.S
0,0 → 1,98
/* Startup code for M68HC11.
Copyright (C) 1999, 2000, 2002 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 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file. (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)
 
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.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
this library does not by itself cause the resulting executable
to be covered by the GNU General Public License.
This exception does not however invalidate any other reasons why
the executable file might be covered by the GNU General Public License. */
;-----------------------------------------
; startup code
;-----------------------------------------
.file "crt0.s"
 
#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
/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)

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.