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, |
®_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 = ®_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) |