mirror of
https://github.com/yann64/haikuports.git
synced 2026-04-11 06:10:06 +02:00
8246 lines
243 KiB
Plaintext
8246 lines
243 KiB
Plaintext
From 0c5f25aaf0fedee6f09bc07507aec23eb6776bdd Mon Sep 17 00:00:00 2001
|
||
From: Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||
Date: Sun, 15 Jan 2017 21:13:16 +0100
|
||
Subject: applying patch gcc6809-4.6.4.patch
|
||
|
||
|
||
diff --git a/README.LW b/README.LW
|
||
new file mode 100644
|
||
index 0000000..bf40e54
|
||
--- /dev/null
|
||
+++ b/README.LW
|
||
@@ -0,0 +1,14 @@
|
||
+This is a port of gcc6809 which is designed to work with the lwtools
|
||
+cross-assembler and linker package. You will need several scripts from that
|
||
+package, available at http://lost.l-w.ca/coco/lwtools/, in order to use
|
||
+this. Instructions for building are present in the lwtools package.
|
||
+
|
||
+This work is based extensively on the gcc6809 4.3.4-3 release by Brian
|
||
+Dominy (brian@oddchange.com) with some significant renovations to make it
|
||
+work with gcc 4.6.1.
|
||
+
|
||
+There is no guarantee that it will work for any particular purpose you
|
||
+choose to put it to.
|
||
+
|
||
+If you run into any problems, contact William Astle (lost@l-w.ca). DO NOT
|
||
+contact the main GCC developers!
|
||
diff --git a/config.sub b/config.sub
|
||
index 204218c..75da021 100755
|
||
--- a/config.sub
|
||
+++ b/config.sub
|
||
@@ -313,7 +313,7 @@ case $basic_machine in
|
||
c6x)
|
||
basic_machine=tic6x-unknown
|
||
;;
|
||
- m6811 | m68hc11 | m6812 | m68hc12 | picochip)
|
||
+ m6809 | m6811 | m68hc11 | m6812 | m68hc12 | picochip)
|
||
# Motorola 68HC11/12.
|
||
basic_machine=$basic_machine-unknown
|
||
os=-none
|
||
@@ -354,7 +354,7 @@ case $basic_machine in
|
||
| i*86-* | i860-* | i960-* | ia64-* \
|
||
| ip2k-* | iq2000-* \
|
||
| lm32-* \
|
||
- | m32c-* | m32r-* | m32rle-* \
|
||
+ | m32c-* | m32r-* | m32rle-* | m6809-* \
|
||
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
|
||
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
||
@@ -509,6 +509,10 @@ case $basic_machine in
|
||
basic_machine=arm-unknown
|
||
os=-cegcc
|
||
;;
|
||
+ coco)
|
||
+ basic_machine=coco
|
||
+ os=-none
|
||
+ ;;
|
||
convex-c1)
|
||
basic_machine=c1-convex
|
||
os=-bsd
|
||
diff --git a/configure b/configure
|
||
index 6be5e9d..33ed6f6 100755
|
||
--- a/configure
|
||
+++ b/configure
|
||
@@ -3439,6 +3439,9 @@ case "${target}" in
|
||
m32r-*-*)
|
||
noconfigdirs="$noconfigdirs ${libgcj}"
|
||
;;
|
||
+ m6809*)
|
||
+ noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}"
|
||
+ ;;
|
||
m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*)
|
||
noconfigdirs="$noconfigdirs target-libstdc++-v3 ${libgcj}"
|
||
libgloss_dir=m68hc11
|
||
diff --git a/configure.ac b/configure.ac
|
||
index ba6d84d..ae8e733 100644
|
||
--- a/configure.ac
|
||
+++ b/configure.ac
|
||
@@ -885,6 +885,9 @@ case "${target}" in
|
||
m32r-*-*)
|
||
noconfigdirs="$noconfigdirs ${libgcj}"
|
||
;;
|
||
+ m6809*)
|
||
+ noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}"
|
||
+ ;;
|
||
m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*)
|
||
noconfigdirs="$noconfigdirs target-libstdc++-v3 ${libgcj}"
|
||
libgloss_dir=m68hc11
|
||
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
|
||
index e0b952f..c2f5318 100644
|
||
--- a/gcc/Makefile.in
|
||
+++ b/gcc/Makefile.in
|
||
@@ -2003,14 +2003,14 @@ $(T)crtbeginT.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
|
||
|
||
# Compile the start modules crt0.o and mcrt0.o that are linked with
|
||
# every program
|
||
-$(T)crt0.o: s-crt0 ; @true
|
||
-$(T)mcrt0.o: s-crt0; @true
|
||
+crt0.o: s-crt0 ; @true
|
||
+mcrt0.o: s-crt0; @true
|
||
|
||
s-crt0: $(CRT0_S) $(MCRT0_S) $(GCC_PASSES) $(CONFIG_H)
|
||
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(CRT0STUFF_T_CFLAGS) \
|
||
- -o $(T)crt0.o -c $(CRT0_S)
|
||
+ -o crt0.o -c $(CRT0_S)
|
||
$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(CRT0STUFF_T_CFLAGS) \
|
||
- -o $(T)mcrt0.o -c $(MCRT0_S)
|
||
+ -o mcrt0.o -c $(MCRT0_S)
|
||
$(STAMP) s-crt0
|
||
#
|
||
# Compiling object files from source files.
|
||
diff --git a/gcc/calls.c b/gcc/calls.c
|
||
index 4ad6c3f..f3af562 100644
|
||
--- a/gcc/calls.c
|
||
+++ b/gcc/calls.c
|
||
@@ -2561,7 +2561,7 @@ expand_call (tree exp, rtx target, int ignore)
|
||
call sequence.
|
||
Also do the adjustments before a throwing call, otherwise
|
||
exception handling can fail; PR 19225. */
|
||
- if (pending_stack_adjust >= 32
|
||
+ if (pending_stack_adjust >= 8
|
||
|| (pending_stack_adjust > 0
|
||
&& (flags & ECF_MAY_BE_ALLOCA))
|
||
|| (pending_stack_adjust > 0
|
||
diff --git a/gcc/config.gcc b/gcc/config.gcc
|
||
index 6dc2427..da0f667 100644
|
||
--- a/gcc/config.gcc
|
||
+++ b/gcc/config.gcc
|
||
@@ -375,6 +375,9 @@ m32r*-*-*)
|
||
cpu_type=m32r
|
||
extra_options="${extra_options} g.opt"
|
||
;;
|
||
+m6809-*-*)
|
||
+ cpu_type=m6809
|
||
+ ;;
|
||
m68k-*-*)
|
||
extra_headers=math-68881.h
|
||
;;
|
||
@@ -1706,6 +1709,12 @@ m32rle-*-linux*)
|
||
thread_file='posix'
|
||
fi
|
||
;;
|
||
+m6809-coco-*)
|
||
+ tmake_file="${tmake_file} m6809/t-m6809 m6809/t-coco"
|
||
+ ;;
|
||
+m6809-*-*)
|
||
+ tmake_file="${tmake_file} m6809/t-m6809 m6809/t-sim"
|
||
+ ;;
|
||
# m68hc11 and m68hc12 share the same machine description.
|
||
m68hc11-*-*|m6811-*-*)
|
||
tm_file="dbxelf.h elfos.h usegas.h newlib-stdint.h m68hc11/m68hc11.h"
|
||
diff --git a/gcc/config/m6809/crt0.S b/gcc/config/m6809/crt0.S
|
||
new file mode 100644
|
||
index 0000000..5f78c82
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/crt0.S
|
||
@@ -0,0 +1,173 @@
|
||
+;;;
|
||
+;;; Copyright 2006, 2007, 2008, 2009 by Brian Dominy <brian@oddchange.com>
|
||
+;;;
|
||
+;;; 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/>.
|
||
+
|
||
+ /* Declare external for main() */
|
||
+ .globl _main
|
||
+
|
||
+
|
||
+/* The startup is heavily dependent on the type of machine and
|
||
+OS environment that is available at the start point.
|
||
+For the most part, the general idea is the same across machines,
|
||
+but the implementation is vastly different. This is managed via
|
||
+conditional compiles throughout the startup code for each of the
|
||
+supported machines. */
|
||
+
|
||
+#ifdef TARGET_COCO /* CoCo memory map */
|
||
+
|
||
+#define COCO_RAMROM_MODE 0xFFDE
|
||
+#define COCO_ALLRAM_MODE 0xFFDF
|
||
+#define COCO_PAGE1 0xFFD5
|
||
+
|
||
+/* SAM M1 and M0 adjusts the memory size */
|
||
+
|
||
+#define BASIC_WARMSTART_FLAG 0x0071
|
||
+#define BASIC_START 0xA027
|
||
+
|
||
+#define __STACK_TOP 0x6800
|
||
+
|
||
+#else /* Simulator (default) memory map */
|
||
+
|
||
+#define SIM_EXIT_REG 0xFF01
|
||
+
|
||
+#define __STACK_TOP 0xFE00
|
||
+
|
||
+#endif
|
||
+
|
||
+
|
||
+ .area .data
|
||
+ .area .ctors
|
||
+ .area .dtors
|
||
+ .area .bss
|
||
+
|
||
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ ;;;
|
||
+ ;;; __exit : Exit point from the program
|
||
+ ;;; For simulation, this writes to a special I/O register that
|
||
+ ;;; the simulator interprets as end-of-program.
|
||
+ ;;;
|
||
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl __exit
|
||
+__exit:
|
||
+#ifdef TARGET_COCO
|
||
+ ;; Go back to ROM/RAM mode
|
||
+ sta COCO_RAMROM_MODE
|
||
+ clr BASIC_WARMSTART_FLAG
|
||
+ jmp BASIC_START
|
||
+#else
|
||
+ tfr x,d
|
||
+ stb SIM_EXIT_REG
|
||
+ bra __exit
|
||
+#endif
|
||
+
|
||
+
|
||
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ ;;;
|
||
+ ;;; __start : Entry point to the program
|
||
+ ;;;
|
||
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl __start
|
||
+__start:
|
||
+
|
||
+#ifdef HAVE_DIRECT
|
||
+ ;; Initialize the direct page pointer
|
||
+ lda #<s_.direct
|
||
+ tfr a,dp
|
||
+#endif
|
||
+
|
||
+#ifdef TARGET_COCO
|
||
+ ;; Turn off interrupts
|
||
+ orcc #(0x10|0x40)
|
||
+
|
||
+ ;; Setup All RAM Mode
|
||
+ sta COCO_ALLRAM_MODE
|
||
+#endif /* TARGET_COCO */
|
||
+
|
||
+ ;; Initialize the stack
|
||
+ lds #__STACK_TOP - 2
|
||
+
|
||
+ ;; Call any "initializer" functions
|
||
+ ldu #s_.ctors
|
||
+__ctors_loop:
|
||
+ ldy ,u++
|
||
+ cmpy #0
|
||
+ beq __ctors_done
|
||
+ jsr ,y
|
||
+ bra __ctors_loop
|
||
+__ctors_done:
|
||
+
|
||
+ ;; Enable interrupts on the simulator
|
||
+#ifndef TARGET_COCO
|
||
+ andcc #~(0x10|0x40)
|
||
+#endif
|
||
+
|
||
+ ;; Set up the environment
|
||
+
|
||
+ ;; Set up argc/argv arrays
|
||
+
|
||
+ ;; Call the main function. The exit code will
|
||
+ ;; be returned in the X register, unless compiled
|
||
+ ;; with -mdret, in which case it comes back in D.
|
||
+ jsr _main
|
||
+
|
||
+ ;; Call any finalizer functions
|
||
+ ldu #s_.dtors
|
||
+__dtors_loop:
|
||
+ ldy ,u++
|
||
+ cmpy #0
|
||
+ beq __dtors_done
|
||
+ jsr ,y
|
||
+ bra __dtors_loop
|
||
+__dtors_done:
|
||
+
|
||
+ ;; If main returns, then invoke _exit() to stop the program
|
||
+ ;; The C library doesn't support -mdret yet, so move the
|
||
+ ;; argument first.
|
||
+#ifdef __DRET__
|
||
+ tfr d,x
|
||
+#endif
|
||
+ jmp _exit
|
||
+
|
||
+
|
||
+
|
||
+ ;;;
|
||
+ ;;; __crt0_vector : Default handler for interrupts
|
||
+ ;;;
|
||
+ .area .text
|
||
+___crt0_vector:
|
||
+ ;; The default behavior is to simply ignore all
|
||
+ ;; non-reset interrupts.
|
||
+ rti
|
||
+
|
||
+
|
||
+ ;;;
|
||
+ ;;; vector : The interrupt vector table
|
||
+ ;;; The linker will ensure that this gets loaded at address 0xFFF0.
|
||
+ ;;;
|
||
+ .area vector
|
||
+vectors:
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word ___crt0_vector
|
||
+ .word __start
|
||
diff --git a/gcc/config/m6809/libgcc1.s b/gcc/config/m6809/libgcc1.s
|
||
new file mode 100644
|
||
index 0000000..4a1e1c7
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/libgcc1.s
|
||
@@ -0,0 +1,511 @@
|
||
+/* libgcc routines for m6809
|
||
+ Copyright (C) 2006 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/>. */
|
||
+
|
||
+/* 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. */
|
||
+
|
||
+
|
||
+#define SIGFPE jmp _abort
|
||
+
|
||
+
|
||
+ ; Shift functions
|
||
+ ; On input, D is value to be shifted, and X has shift count.
|
||
+ ; Result is also in D.
|
||
+
|
||
+#ifdef L_ashlhi3
|
||
+ .area .text
|
||
+ .globl _ashlhi3
|
||
+_ashlhi3:
|
||
+ pshs x
|
||
+1$:
|
||
+ leax -1,x
|
||
+ cmpx #-1
|
||
+ beq 2$
|
||
+ aslb
|
||
+ rola
|
||
+ bra 1$
|
||
+2$:
|
||
+ puls x,pc
|
||
+#endif
|
||
+
|
||
+#ifdef L_ashrhi3
|
||
+ .area .text
|
||
+ .globl _ashrhi3
|
||
+_ashrhi3:
|
||
+ pshs x
|
||
+1$:
|
||
+ leax -1,x
|
||
+ cmpx #-1
|
||
+ beq 2$
|
||
+ asra
|
||
+ rorb
|
||
+ bra 1$
|
||
+2$:
|
||
+ puls x,pc
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_lshrhi3
|
||
+ .area .text
|
||
+ .globl _lshrhi3
|
||
+_lshrhi3:
|
||
+ pshs x
|
||
+1$:
|
||
+ leax -1,x
|
||
+ cmpx #-1
|
||
+ beq 2$
|
||
+ lsra
|
||
+ rorb
|
||
+ bra 1$
|
||
+2$:
|
||
+ puls x,pc
|
||
+#endif
|
||
+
|
||
+
|
||
+
|
||
+#ifdef L_softregs
|
||
+ .area direct
|
||
+ .globl m0, m1, m2, m3, m4, m5, m6, m7
|
||
+ .globl m8, m9, m10, m11, m12, m13, m14, m15
|
||
+m0: .blkb 1
|
||
+m1: .blkb 1
|
||
+m2: .blkb 1
|
||
+m3: .blkb 1
|
||
+m4: .blkb 1
|
||
+m5: .blkb 1
|
||
+m6: .blkb 1
|
||
+m7: .blkb 1
|
||
+m8: .blkb 1
|
||
+m9: .blkb 1
|
||
+m10: .blkb 1
|
||
+m11: .blkb 1
|
||
+m12: .blkb 1
|
||
+m13: .blkb 1
|
||
+m14: .blkb 1
|
||
+m15: .blkb 1
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_ashlsi3_one
|
||
+ .area .text
|
||
+ .globl _ashlsi3_one
|
||
+_ashlsi3_one:
|
||
+ asl 3,x
|
||
+ rol 2,x
|
||
+ rol 1,x
|
||
+ rol ,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+#ifdef L_ashlsi3
|
||
+ /* X points to the SImode (source/dest)
|
||
+ B is the count */
|
||
+_ashlsi3:
|
||
+ pshs u
|
||
+ cmpb #16
|
||
+ blt try8
|
||
+ subb #16
|
||
+ ; Shift by 16
|
||
+ ldu 2,x
|
||
+ stu ,x
|
||
+try8:
|
||
+ cmpb #8
|
||
+ blt try_rest
|
||
+ subb #8
|
||
+ ; Shift by 8
|
||
+
|
||
+try_rest:
|
||
+ tstb
|
||
+ beq done
|
||
+do_rest:
|
||
+ ; Shift by 1
|
||
+ asl 3,x
|
||
+ rol 2,x
|
||
+ rol 1,x
|
||
+ rol ,x
|
||
+ decb
|
||
+ bne do_rest
|
||
+done:
|
||
+ puls u,pc
|
||
+#endif
|
||
+
|
||
+#ifdef L_ashrsi3_one
|
||
+ .area .text
|
||
+ .globl _ashlsi3_one
|
||
+_ashrsi3_one:
|
||
+ asr ,x
|
||
+ ror 1,x
|
||
+ ror 2,x
|
||
+ ror 3,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_lshrsi3_one
|
||
+ .area .text
|
||
+ .globl _lshrsi3_one
|
||
+_lshrsi3_one:
|
||
+ lsr ,x
|
||
+ ror 1,x
|
||
+ ror 2,x
|
||
+ ror 3,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_clzsi2
|
||
+ .area .text
|
||
+ .globl ___clzhi2
|
||
+ ; Input: X = 16-bit unsigned integer
|
||
+ ; Output: X = number of leading zeros
|
||
+ ; This function destroys the value in D.
|
||
+___clzhi2:
|
||
+ pshs x
|
||
+ ; Find the offset of the leftmost '1' bit in
|
||
+ ; the left half of the word.
|
||
+ ;
|
||
+ ; Bits are numbered in the table with 1 meaning the
|
||
+ ; LSB and 8 meaning the MSB.
|
||
+ ;
|
||
+ ; If nonzero, then clz is 8-a.
|
||
+ tfr x,d
|
||
+ ldx #___clz_tab
|
||
+ tfr a,b
|
||
+ clra
|
||
+ ldb d,x
|
||
+ bne upper_bit_set
|
||
+
|
||
+lower_bit_set:
|
||
+ ; If the upper byte is zero, then check the lower
|
||
+ ; half of the word. Return 16-a.
|
||
+ puls d
|
||
+ clra
|
||
+ ldb d,x
|
||
+ negb
|
||
+ addb #16
|
||
+ bra done
|
||
+
|
||
+upper_bit_set:
|
||
+ negb
|
||
+ addb #8
|
||
+ puls x
|
||
+
|
||
+done:
|
||
+ tfr d,x
|
||
+ puls pc
|
||
+#endif
|
||
+
|
||
+#ifdef L_clzdi2
|
||
+ .area .text
|
||
+ .globl ___clzsi2
|
||
+ ; Input: 32-bit unsigned integer is on the stack, just
|
||
+ ; above the return address
|
||
+ ; Output: X = number of leading zeros
|
||
+___clzsi2:
|
||
+ ; Check the upper 16-bit word
|
||
+ ; If it is not zero, then return clzhi2(X).
|
||
+ ; A branch can be used instead of a call since no
|
||
+ ; postprocessing is needed. Use long branch form
|
||
+ ; though since functions may not be near each other.
|
||
+ ldx 2,s
|
||
+ lbne ___clzhi2
|
||
+ ldx 4,s
|
||
+ jsr ___clzhi2
|
||
+ leax 16,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+#ifdef L_ctzsi2
|
||
+ .area .text
|
||
+ .globl ___ctzhi2
|
||
+ ; Input: X = 16-bit unsigned integer
|
||
+ ; Output: X = number of trailing zeros
|
||
+ ; F(x) = 15 - clzhi2(X & -x)
|
||
+ ; This function destroys the value in D.
|
||
+___ctzhi2:
|
||
+ tfr x,d
|
||
+ coma
|
||
+ comb
|
||
+ addd #1
|
||
+ pshs a
|
||
+ pshs b
|
||
+ tfr x,d
|
||
+ andb ,s+
|
||
+ anda ,s+
|
||
+ tfr d,x
|
||
+ jsr ___clzhi2
|
||
+ tfr x,d
|
||
+ subd #16
|
||
+ coma
|
||
+ comb
|
||
+ tfr d,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_ctzdi2
|
||
+ .area .text
|
||
+ .globl ___ctzsi2
|
||
+ ; Input: 32-bit unsigned integer is on the stack, just
|
||
+ ; above the return address
|
||
+ ; Output: X = number of leading zeros
|
||
+___ctzsi2:
|
||
+ ; Check the lower 16-bit word
|
||
+ ; If it is not zero, then return ctzhi2(X).
|
||
+ ; A branch can be used instead of a call since no
|
||
+ ; postprocessing is needed. Use long branch form
|
||
+ ; though since functions may not be near each other.
|
||
+ ldx 4,s
|
||
+ lbne ___ctzhi2
|
||
+ ldx 2,s
|
||
+ jsr ___ctzhi2
|
||
+ leax 16,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_mulhi3
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+;;; ___mulhi3 - signed/unsigned multiply
|
||
+;;; Called by GCC to implement 16x16 multiplication
|
||
+;;; Arguments: Two 16-bit values, one in stack, one in X.
|
||
+;;; Result: 16-bit result in X
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl _mulhi3
|
||
+_mulhi3:
|
||
+ pshs x
|
||
+ lda 5,s ; left msb * right lsb * 256
|
||
+ ldb ,s
|
||
+ mul
|
||
+ tfr b,a
|
||
+ clrb
|
||
+ tfr d,x
|
||
+ ldb 1,s ; left lsb * right msb * 256
|
||
+ lda 4,s
|
||
+ mul
|
||
+ tfr b,a
|
||
+ clrb
|
||
+ leax d,x
|
||
+ ldb 1,s ; left lsb * right lsb
|
||
+ lda 5,s
|
||
+ mul
|
||
+ leax d,x
|
||
+ puls d,pc ; kill D to remove initial push
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_divhi3
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+;;; ___divhi3 - signed division
|
||
+;;; Arguments: Dividend in X, divisor on the stack
|
||
+;;; Returns result in X.
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl _divhi3
|
||
+_divhi3:
|
||
+ ldd 2,s
|
||
+ bne do_div ; check dividend
|
||
+ SIGFPE
|
||
+do_div:
|
||
+ pshs x
|
||
+ jsr _seuclid
|
||
+ puls x,pc
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_modhi3
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+;;; ___modhi3 - signed modulo
|
||
+;;; Arguments: Dividend in X, divisor on the stack
|
||
+;;; Returns result in X.
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl _modhi3
|
||
+_modhi3:
|
||
+ ldd 2,s
|
||
+ bne do_mod ; check dividend
|
||
+ SIGFPE
|
||
+do_mod:
|
||
+ pshs x
|
||
+ jsr _seuclid
|
||
+ leas 2,s
|
||
+ tfr d,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+
|
||
+#ifdef L_udivhi3
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+;;; ___udivhi3 - unsigned division
|
||
+;;; Arguments: Dividend in X, divisor on the stack
|
||
+;;; Returns result in X.
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl _udivhi3
|
||
+_udivhi3:
|
||
+ ldd 2,s
|
||
+ bne do_udiv ; check dividend
|
||
+ SIGFPE
|
||
+do_udiv:
|
||
+ pshs x
|
||
+ jsr _euclid
|
||
+ puls x,pc
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_umodhi3
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+;;; ___umodhi3 - unsigned modulo
|
||
+;;; Arguments: Dividend in X, divisor on the stack
|
||
+;;; Returns result in X.
|
||
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
+ .area .text
|
||
+ .globl _umodhi3
|
||
+_umodhi3:
|
||
+ ldd 2,s
|
||
+ bne do_umod ; check dividend
|
||
+ SIGFPE
|
||
+do_umod:
|
||
+ pshs x
|
||
+ jsr _euclid
|
||
+ leas 2,s
|
||
+ tfr d,x
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+#ifdef L_euclid
|
||
+; unsigned euclidean division
|
||
+; calling: (left / right)
|
||
+; push left
|
||
+; ldd right
|
||
+; jsr _euclid
|
||
+; quotient on the stack (left)
|
||
+; modulus in d
|
||
+
|
||
+ .area .text
|
||
+ .globl _euclid
|
||
+ left=5
|
||
+ right=1 ; word
|
||
+ count=0 ; byte
|
||
+ CARRY=1 ; alias
|
||
+_euclid:
|
||
+ leas -3,s ; 2 local variables
|
||
+ clr count,s ; prescale divisor
|
||
+ inc count,s
|
||
+ tsta
|
||
+presc:
|
||
+ bmi presc_done
|
||
+ inc count,s
|
||
+ aslb
|
||
+ rola
|
||
+ bra presc
|
||
+presc_done:
|
||
+ std right,s
|
||
+ ldd left,s
|
||
+ clr left,s ; quotient = 0
|
||
+ clr left+1,s
|
||
+mod1:
|
||
+ subd right,s ; check subtract
|
||
+ bcc mod2
|
||
+ addd right,s
|
||
+ andcc #~CARRY
|
||
+ bra mod3
|
||
+mod2:
|
||
+ orcc #CARRY
|
||
+mod3:
|
||
+ rol left+1,s ; roll in carry
|
||
+ rol left,s
|
||
+ lsr right,s
|
||
+ ror right+1,s
|
||
+ dec count,s
|
||
+ bne mod1
|
||
+ leas 3,s
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+#ifdef L_seuclid
|
||
+; signed euclidean division
|
||
+; calling: (left / right)
|
||
+; push left
|
||
+; ldd right
|
||
+; jsr _seuclid
|
||
+; quotient on the stack (left)
|
||
+; modulus in d
|
||
+ .area .text
|
||
+ .globl _seuclid
|
||
+ left=6
|
||
+ right=2
|
||
+ quot_sign=1
|
||
+ mod_sign=0
|
||
+_seuclid:
|
||
+ leas -4,s ; 3 local variables
|
||
+ std right,s
|
||
+ clr mod_sign,s
|
||
+ clr quot_sign,s
|
||
+ ldd left,s
|
||
+ bge mod_abs
|
||
+ inc mod_sign,s ; sign(mod) = sign(left)
|
||
+ inc quot_sign,s
|
||
+ bsr negd ; abs(left) -> D
|
||
+mod_abs:
|
||
+ pshs b,a ; push abs(left)
|
||
+ ldd right+2,s ; all references shifted by 2
|
||
+ bge quot_abs
|
||
+ dec quot_sign+2,s ; sign(quot) = sign(left) XOR sign(right)
|
||
+ bsr negd ; abs(right) -> D
|
||
+quot_abs:
|
||
+ jsr _euclid ; call (unsigned) euclidean division
|
||
+ std right+2,s
|
||
+ puls a,b ; quot -> D
|
||
+ tst quot_sign,s ; all references no longer shifted
|
||
+ beq quot_done
|
||
+ bsr negd
|
||
+quot_done:
|
||
+ std left,s ; quot -> left
|
||
+ ldd right,s
|
||
+ tst mod_sign,s
|
||
+ beq mod_done
|
||
+ bsr negd
|
||
+mod_done:
|
||
+ leas 4,s ; destroy stack frame
|
||
+ rts
|
||
+
|
||
+negd: ; self-explanatory !
|
||
+ nega
|
||
+ negb
|
||
+ sbca #0
|
||
+ rts
|
||
+#endif
|
||
+
|
||
+
|
||
+
|
||
+#ifdef L_pending_addsi3
|
||
+_addsi3:
|
||
+ rts
|
||
+#endif /* L_pending_addsi3 */
|
||
+
|
||
+
|
||
+
|
||
diff --git a/gcc/config/m6809/m6809-protos.h b/gcc/config/m6809/m6809-protos.h
|
||
new file mode 100644
|
||
index 0000000..ba00de5
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/m6809-protos.h
|
||
@@ -0,0 +1,94 @@
|
||
+/* GCC for 6809 : machine-specific function prototypes
|
||
+
|
||
+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/>. */
|
||
+
|
||
+#ifndef __M6809_PROTOS_H__
|
||
+#define __M6809_PROTOS_H__
|
||
+
|
||
+void print_options (FILE *file);
|
||
+void m6809_cpu_cpp_builtins (void);
|
||
+void m6809_override_options (void);
|
||
+void m6809_init_builtins (void);
|
||
+unsigned int m6809_get_live_regs (void);
|
||
+const char * m6809_get_regs_printable (unsigned int regs);
|
||
+unsigned int m6809_get_regs_size (unsigned int regs);
|
||
+int m6809_function_has_type_attr_p (tree decl, const char *);
|
||
+int m6809_current_function_has_type_attr_p (const char *);
|
||
+int prologue_epilogue_required (void);
|
||
+int noreturn_functionp (rtx x);
|
||
+void output_function_prologue (FILE *file, int size);
|
||
+void output_function_epilogue (FILE *file, int size);
|
||
+int check_float_value (enum machine_mode mode, double *d, int overflow);
|
||
+void m6809_asm_named_section (const char *name, unsigned int flags, tree decl);
|
||
+void m6809_asm_file_start (void);
|
||
+void m6809_output_ascii (FILE *fp, const char *str, unsigned long size);
|
||
+void m6809_declare_function_name (FILE *asm_out_file, const char *name, tree decl);
|
||
+void m6809_reorg (void);
|
||
+int m6809_current_function_is_void (void);
|
||
+int m6809_can_merge_pushpop_p (int op, int regs1, int regs2);
|
||
+int m6809_function_value_regno_p (unsigned int regno);
|
||
+void emit_prologue_insns (void);
|
||
+void emit_epilogue_insns (bool);
|
||
+void m6809_conditional_register_usage (void);
|
||
+void m6809_output_quoted_string (FILE *asm_file, const char *string);
|
||
+int m6809_match_peephole2 (unsigned int peephole_id, unsigned int stage);
|
||
+int m6809_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode);
|
||
+int power_of_two_p (unsigned int n);
|
||
+void m6809_do_casesi (rtx index, rtx lower_bound, rtx range, rtx table_label, rtx default_label);
|
||
+void m6809_output_addsi3 (int rtx_code, rtx *operands);
|
||
+rtx m6809_function_arg_on_stack (CUMULATIVE_ARGS *cump);
|
||
+void expand_constant_shift (int code, rtx dst, rtx src, rtx count);
|
||
+int m6809_single_operand_operator (rtx exp);
|
||
+
|
||
+#ifdef TREE_CODE
|
||
+int m6809_init_cumulative_args (CUMULATIVE_ARGS cum, tree fntype, rtx libname);
|
||
+#endif /* TREE_CODE */
|
||
+
|
||
+#ifdef RTX_CODE
|
||
+void print_direct_prefix (FILE *file, rtx addr);
|
||
+void print_operand (FILE *file, rtx x, int code);
|
||
+void print_operand_address (FILE *file, rtx addr);
|
||
+void notice_update_cc (rtx exp, rtx insn);
|
||
+enum reg_class m6809_preferred_reload_class (rtx x, enum reg_class regclass);
|
||
+rtx gen_rtx_const_high (rtx r);
|
||
+rtx gen_rtx_const_low (rtx r);
|
||
+rtx gen_rtx_register_pushpop (int pop_flag, int regs);
|
||
+void emit_libcall_insns (enum machine_mode mode, const char *name, rtx *operands, int count);
|
||
+const char * output_branch_insn (enum rtx_code code, rtx *operands, int length);
|
||
+void output_far_call_insn (rtx *operands, int has_return);
|
||
+void m6809_initialize_trampoline (rtx tramp, tree fndecl, rtx cxt);
|
||
+rtx m6809_expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, int ignore);
|
||
+const char * far_functionp (rtx x);
|
||
+rtx m6809_function_value (const tree valtype, const tree func);
|
||
+void m6809_output_shift_insn (int rtx_code, rtx *operands);
|
||
+
|
||
+const char * m6809_get_decl_bank (tree decl);
|
||
+void output_branch_insn1 (const char *opcode, rtx *operands, int long_p);
|
||
+rtx m6809_builtin_operand (tree arglist, enum machine_mode mode, int opnum);
|
||
+const char * far_function_type_p (tree type);
|
||
+void m6809_asm_trampoline_template(FILE *f);
|
||
+bool m6809_frame_pointer_required (void);
|
||
+int m6809_can_eliminate (int from, int to);
|
||
+int m6809_initial_elimination_offset (int from, int to);
|
||
+void m6809_emit_move_insn (rtx dst, rtx src);
|
||
+void m6809_split_shift (enum rtx_code code, rtx *operands);
|
||
+bool m6809_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED);
|
||
+
|
||
+
|
||
+#endif /* RTX_CODE */
|
||
+
|
||
+#endif /* __M6809_PROTOS_H__ */
|
||
diff --git a/gcc/config/m6809/m6809.c b/gcc/config/m6809/m6809.c
|
||
new file mode 100644
|
||
index 0000000..721583e
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/m6809.c
|
||
@@ -0,0 +1,3023 @@
|
||
+/*-------------------------------------------------------------------
|
||
+ FILE: m6809.c
|
||
+-------------------------------------------------------------------*/
|
||
+/* Subroutines for insn-output.c for MC6809.
|
||
+ Copyright (C) 1989-2007 Free Software Foundation, Inc.
|
||
+
|
||
+ MC6809 Version by Tom Jones (jones@sal.wisc.edu)
|
||
+ Space Astronomy Laboratory
|
||
+ University of Wisconsin at Madison
|
||
+
|
||
+ minor changes to adapt it to gcc-2.5.8 by Matthias Doerfel
|
||
+ ( msdoerfe@informatik.uni-erlangen.de )
|
||
+ also added #pragma interrupt (inspired by gcc-6811)
|
||
+
|
||
+ minor changes to adapt it to gcc-2.8.0 by Eric Botcazou
|
||
+ (ebotcazou@multimania.com)
|
||
+
|
||
+ minor changes to adapt it to gcc-2.95.3 by Eric Botcazou
|
||
+ (ebotcazou@multimania.com)
|
||
+
|
||
+ major cleanup, improvements, and upgrade to gcc 3.4 by Brian Dominy
|
||
+ (brian@oddchange.com)
|
||
+
|
||
+ additional adjustments, etc., for gcc 4.6.1 by William Astle (lost@l-w.ca)
|
||
+
|
||
+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/>. */
|
||
+
|
||
+#include <string.h>
|
||
+#include <time.h>
|
||
+#include <sys/types.h>
|
||
+#include <sys/timeb.h>
|
||
+#include <stdio.h>
|
||
+#include "config.h"
|
||
+#include "system.h"
|
||
+#include "coretypes.h"
|
||
+#include "tm.h"
|
||
+#include "tree.h"
|
||
+#include "rtl.h"
|
||
+#include "tm_p.h"
|
||
+#include "regs.h"
|
||
+#include "flags.h"
|
||
+#include "hard-reg-set.h"
|
||
+#include "real.h"
|
||
+#include "tree.h"
|
||
+#include "insn-config.h"
|
||
+#include "conditions.h"
|
||
+#include "insn-flags.h"
|
||
+#include "output.h"
|
||
+#include "insn-attr.h"
|
||
+#include "function.h"
|
||
+#include "target.h"
|
||
+#include "target-def.h"
|
||
+#include "expr.h"
|
||
+#include "recog.h"
|
||
+#include "cpplib.h"
|
||
+#include "c-family/c-pragma.h"
|
||
+#include "c-family/c-common.h"
|
||
+#include "toplev.h"
|
||
+#include "optabs.h"
|
||
+#include "version.h"
|
||
+#include "df.h"
|
||
+#include "rtlhooks-def.h"
|
||
+
|
||
+/* macro to return TRUE if length of operand mode is one byte */
|
||
+#define BYTE_MODE(X) ((GET_MODE_SIZE (GET_MODE (X))) == 1)
|
||
+
|
||
+
|
||
+/* REAL_REG_P(x) is a true if the rtx 'x' represents a real CPU
|
||
+register and not a fake one that is emulated in software. */
|
||
+#define REAL_REG_P(x) (REG_P(x) && !M_REG_P(x))
|
||
+
|
||
+/*-------------------------------------------------------------------
|
||
+ Target hooks, moved from target.h
|
||
+-------------------------------------------------------------------*/
|
||
+static void m6809_encode_section_info (tree decl, rtx rtl, int new_decl_p ATTRIBUTE_UNUSED);
|
||
+
|
||
+#undef TARGET_ENCODE_SECTION_INFO
|
||
+#define TARGET_ENCODE_SECTION_INFO m6809_encode_section_info
|
||
+
|
||
+#undef TARGET_ASM_FILE_START
|
||
+#define TARGET_ASM_FILE_START m6809_asm_file_start
|
||
+
|
||
+#undef TARGET_ASM_ALIGNED_HI_OP
|
||
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
|
||
+
|
||
+#undef TARGET_ASM_ALIGNED_SI_OP
|
||
+#define TARGET_ASM_ALIGNED_SI_OP NULL
|
||
+
|
||
+#undef TARGET_ASM_UNALIGNED_HI_OP
|
||
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.word\t"
|
||
+
|
||
+#undef TARGET_ASM_UNALIGNED_SI_OP
|
||
+#define TARGET_ASM_UNALIGNED_SI_OP NULL
|
||
+
|
||
+#undef TARGET_RTX_COSTS
|
||
+#define TARGET_RTX_COSTS m6809_rtx_costs
|
||
+
|
||
+#undef TARGET_ATTRIBUTE_TABLE
|
||
+#define TARGET_ATTRIBUTE_TABLE m6809_attribute_table
|
||
+
|
||
+#undef TARGET_INIT_BUILTINS
|
||
+#define TARGET_INIT_BUILTINS m6809_init_builtins
|
||
+
|
||
+#undef TARGET_EXPAND_BUILTIN
|
||
+#define TARGET_EXPAND_BUILTIN m6809_expand_builtin
|
||
+
|
||
+#undef TARGET_DEFAULT_TARGET_FLAGS
|
||
+#define TARGET_DEFAULT_TARGET_FLAGS (MASK_REG_ARGS | MASK_DIRECT)
|
||
+
|
||
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
||
+#define TARGET_FUNCTION_OK_FOR_SIBCALL m6809_function_ok_for_sibcall
|
||
+
|
||
+#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
|
||
+#define TARGET_ASM_TRAMPOLINE_TEMPLATE m6809_asm_trampoline_template
|
||
+
|
||
+#undef TARGET_TRAMPOLINE_INIT
|
||
+#define TARGET_TRAMPOLINE_INIT m6809_initialize_trampoline
|
||
+
|
||
+#undef TARGET_FRAME_POINTER_REQUIRED
|
||
+#define TARGET_FRAME_POINTER_REQUIRED m6809_frame_pointer_required
|
||
+
|
||
+#undef TARGET_OPTION_OVERRIDE
|
||
+#define TARGET_OPTION_OVERRIDE m6809_override_options
|
||
+
|
||
+/* External variables used */
|
||
+extern int reload_completed; /* set in toplev.c */
|
||
+extern FILE *asm_out_file;
|
||
+
|
||
+static int last_mem_size; /* operand size (bytes) */
|
||
+
|
||
+/* True if the section was recently changed and another .area
|
||
+ * directive needs to be output before emitting the next label. */
|
||
+int section_changed = 0;
|
||
+
|
||
+/* Section names. The defaults here are used until an
|
||
+ * __attribute__((section)) is seen that changes it. */
|
||
+char code_section_op[128] = "\t.area .text";
|
||
+char data_section_op[128] = "\t.area .data";
|
||
+char bss_section_op[128] = "\t.area .bss";
|
||
+const char *code_bank_option = 0;
|
||
+
|
||
+/* TRUE if the direct mode prefix might be valid in this context.
|
||
+ * This is set by 'print_address' prior to calling output_addr_const,
|
||
+ * which performs into 'print_direct_prefix' to do the final checks. */
|
||
+static int check_direct_prefix_flag;
|
||
+
|
||
+/* Nonzero if an address is being printed in a context which does not
|
||
+ * permit any PIC modifications to the address */
|
||
+static int pic_ok_for_addr_p = 1;
|
||
+
|
||
+/* Current code page. This supports machines which can do bank
|
||
+ * switching to allow for more than 64KB of code/data. */
|
||
+char far_code_page[64];
|
||
+
|
||
+/* Current bank name */
|
||
+static char current_bank_name[8] = "-1";
|
||
+
|
||
+/* Default bank name */
|
||
+static char default_code_bank_name[8] = "-1";
|
||
+
|
||
+/* Direct memory reserved as soft registers */
|
||
+unsigned int m6809_soft_regs = 0;
|
||
+
|
||
+/* ABI version */
|
||
+unsigned int m6809_abi_version = M6809_ABI_VERSION_REGS;
|
||
+
|
||
+
|
||
+/**
|
||
+ * Called after options have been parsed.
|
||
+ * If overrides have been specified on the command-line, then
|
||
+ * these values are copied into the main storage variables.
|
||
+ */
|
||
+void
|
||
+m6809_override_options (void)
|
||
+{
|
||
+ /* Handle -mfar-code-page */
|
||
+ if (far_code_page_option == 0)
|
||
+ far_code_page_option = "__default_code_page";
|
||
+ strcpy (far_code_page, far_code_page_option);
|
||
+
|
||
+ /* Handle -mcode-section, -mdata-section, and -mbss-section */
|
||
+ if (code_section_ptr != 0)
|
||
+ sprintf (code_section_op, "\t.area %s", code_section_ptr);
|
||
+ if (data_section_ptr != 0)
|
||
+ sprintf (data_section_op, "\t.area %s", data_section_ptr);
|
||
+ if (bss_section_ptr != 0)
|
||
+ sprintf (bss_section_op, "\t.area %s", bss_section_ptr);
|
||
+
|
||
+ /* Handle -mcode-bank */
|
||
+ if (code_bank_option != 0)
|
||
+ sprintf (default_code_bank_name, "%s", code_bank_option);
|
||
+
|
||
+ /* Handle -mabi-version or -mno-reg-args */
|
||
+ if (m6809_abi_version_ptr != 0)
|
||
+ {
|
||
+ if (!strcmp (m6809_abi_version_ptr, "stack"))
|
||
+ m6809_abi_version = M6809_ABI_VERSION_STACK;
|
||
+ else if (!strcmp (m6809_abi_version_ptr, "regs"))
|
||
+ m6809_abi_version = M6809_ABI_VERSION_REGS;
|
||
+ else if (!strcmp (m6809_abi_version_ptr, "bx"))
|
||
+ m6809_abi_version = M6809_ABI_VERSION_BX;
|
||
+ else if (!strcmp (m6809_abi_version_ptr, "latest"))
|
||
+ m6809_abi_version = M6809_ABI_VERSION_LATEST;
|
||
+ else
|
||
+ m6809_abi_version = atoi (m6809_abi_version_ptr);
|
||
+ }
|
||
+
|
||
+ /* The older -mno-reg-args option is deprecated, and treated
|
||
+ as -mabi=stack. */
|
||
+ if (!TARGET_REG_ARGS)
|
||
+ {
|
||
+ warning (WARNING_OPT "-mno-reg-args deprecated; use -mabi=stack instead.");
|
||
+ m6809_abi_version = M6809_ABI_VERSION_STACK;
|
||
+ }
|
||
+
|
||
+ /* -fexceptions is unsupported */
|
||
+ flag_exceptions = 0;
|
||
+ flag_non_call_exceptions = 0;
|
||
+ flag_unwind_tables = 0;
|
||
+}
|
||
+
|
||
+
|
||
+/**
|
||
+ * Output prefix that directs the assembler to use a direct-mode
|
||
+ * instruction if globally enabled, address is a symbol, and symbol
|
||
+ * has been marked as in direct page. Also, never do this if
|
||
+ * using the indirect mode. */
|
||
+void
|
||
+print_direct_prefix (FILE * file, rtx addr)
|
||
+{
|
||
+ if (TARGET_DIRECT &&
|
||
+ (GET_CODE (addr) == SYMBOL_REF) &&
|
||
+ SYMBOL_REF_FLAG (addr) &&
|
||
+ check_direct_prefix_flag)
|
||
+ {
|
||
+ putc ('*', file);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/** Prints an operand (that is not an address) in assembly from RTL. */
|
||
+void
|
||
+print_operand (FILE * file, rtx x, int code)
|
||
+{
|
||
+ if (REG_P (x)) {
|
||
+ /* gcc currently allocates the entire 16-bit 'd' register
|
||
+ * even when it only needs an 8-bit value. So here it
|
||
+ * is tricked into printing only the lower 8-bit 'b'
|
||
+ * register into the assembly output.
|
||
+ *
|
||
+ * Eventually gcc should be modified to allocate a/b
|
||
+ * independently and this hack can be removed.
|
||
+ *
|
||
+ * Occasionally, we may want to do an operation using
|
||
+ * the 'a' register instead of 'b'; use the 'A' code
|
||
+ * to specify that.
|
||
+ */
|
||
+ if (code == 'A')
|
||
+ fputs ("a", file);
|
||
+ else if ((BYTE_MODE (x)) && (REGNO (x) == HARD_D_REGNUM))
|
||
+ fputs ("b", file);
|
||
+ else if (M_REG_P (x) && code == 'L')
|
||
+ /* Soft registers can be treated like memory and accessed
|
||
+ * at a particular offset. TODO : handle 'W' */
|
||
+ fputs (reg_names[REGNO (x)+1], file);
|
||
+ else
|
||
+ fputs (reg_names[REGNO (x)], file);
|
||
+ }
|
||
+
|
||
+ else if (MEM_P (x)) {
|
||
+ last_mem_size = GET_MODE_SIZE (GET_MODE (x));
|
||
+ if (code == 'L') { /* LSH of word address */
|
||
+ if (GET_CODE (XEXP (x, 0)) == MEM)
|
||
+ {
|
||
+ /* Offseting an indirect addressing mode is not supported */
|
||
+ error ("expression too complex for 6809 (offset indirect mode)");
|
||
+ debug_rtx (x);
|
||
+ }
|
||
+ else
|
||
+ x = adjust_address (x, QImode, 1);
|
||
+ }
|
||
+ else if (code == 'M') { /* MSH of word address */
|
||
+ if (GET_CODE (XEXP (x, 0)) == MEM)
|
||
+ {
|
||
+ /* Offseting an indirect addressing mode is not supported */
|
||
+ error ("expression too complex for 6809 (offset indirect mode)");
|
||
+ debug_rtx (x);
|
||
+ }
|
||
+ else
|
||
+ x = adjust_address (x, QImode, 0);
|
||
+ }
|
||
+ else if (code == 'W') { /* least significant half of 32-bit */
|
||
+ x = adjust_address (x, HImode, 2);
|
||
+ }
|
||
+
|
||
+ pic_ok_for_addr_p = (code != 'C');
|
||
+ output_address (XEXP (x, 0));
|
||
+ }
|
||
+
|
||
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) {
|
||
+ union { double d; int i[2]; } u;
|
||
+ u.i[0] = CONST_DOUBLE_LOW (x);
|
||
+ u.i[1] = CONST_DOUBLE_HIGH (x);
|
||
+ fprintf (file, "#%#9.9g", u.d);
|
||
+ }
|
||
+
|
||
+ else if (code == 'R') {
|
||
+ fprintf (file, "%s",
|
||
+ m6809_get_regs_printable (INTVAL (x)));
|
||
+ }
|
||
+
|
||
+ else {
|
||
+ if (code == 'L') { /* LSH of word address */
|
||
+ x = gen_rtx_CONST_INT (VOIDmode, (INTVAL(x) & 0xff));
|
||
+ }
|
||
+ else if (code == 'M') { /* MSH of word address */
|
||
+ x = gen_rtx_CONST_INT (VOIDmode, ((INTVAL(x) >> 8) & 0xff));
|
||
+ }
|
||
+
|
||
+ putc ('#', file);
|
||
+ output_addr_const (file, x);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/** Prints an address operand to assembler from its RTL representation. */
|
||
+void
|
||
+print_operand_address (FILE *file, rtx addr)
|
||
+{
|
||
+ register rtx base = 0;
|
||
+ register rtx offset = 0;
|
||
+ int regno;
|
||
+ int indirect_flag = 0;
|
||
+
|
||
+ check_direct_prefix_flag = 0;
|
||
+
|
||
+ /*** check for indirect addressing ***/
|
||
+ if (MEM_P (addr)) {
|
||
+ last_mem_size = GET_MODE_SIZE (GET_MODE (addr));
|
||
+ addr = XEXP (addr, 0);
|
||
+ if (pic_ok_for_addr_p)
|
||
+ {
|
||
+ indirect_flag = 1;
|
||
+ fprintf (file, "[");
|
||
+ }
|
||
+ }
|
||
+
|
||
+
|
||
+ switch (GET_CODE (addr)) {
|
||
+ case REG:
|
||
+ regno = REGNO (addr);
|
||
+ fprintf (file, ",%s", reg_names[regno]);
|
||
+ break;
|
||
+
|
||
+ case PRE_DEC:
|
||
+ regno = REGNO (XEXP (addr, 0));
|
||
+ fputs (((last_mem_size == 1) ? ",-" : ",--"), file);
|
||
+ fprintf (file, "%s", reg_names[regno]);
|
||
+ break;
|
||
+
|
||
+ case POST_INC:
|
||
+ regno = REGNO (XEXP (addr, 0));
|
||
+ fprintf (file, ",%s", reg_names[regno]);
|
||
+ fputs (((last_mem_size == 1) ? "+" : "++"), file);
|
||
+ break;
|
||
+
|
||
+ case PLUS:
|
||
+ base = XEXP (addr, 0);
|
||
+ if (MEM_P (base))
|
||
+ base = XEXP (base, 0);
|
||
+
|
||
+ offset = XEXP (addr, 1);
|
||
+ if (MEM_P (offset))
|
||
+ offset = XEXP (offset, 0);
|
||
+
|
||
+ if ((CONSTANT_ADDRESS_P (base)) && (CONSTANT_ADDRESS_P (offset))) {
|
||
+ if (!indirect_flag)
|
||
+ check_direct_prefix_flag = 1;
|
||
+ output_addr_const (file, base);
|
||
+ check_direct_prefix_flag = 0;
|
||
+ fputs ("+", file);
|
||
+ output_addr_const (file, offset);
|
||
+ }
|
||
+
|
||
+ else if ((CONSTANT_ADDRESS_P (base)) && (A_REG_P (offset))) {
|
||
+ output_addr_const (file, base);
|
||
+ fprintf (file, ",%s", reg_names[REGNO (offset)]);
|
||
+ }
|
||
+
|
||
+ else if ((CONSTANT_ADDRESS_P (offset)) && (A_REG_P (base))) {
|
||
+ output_addr_const (file, offset);
|
||
+ fprintf (file, ",%s", reg_names[REGNO (base)]);
|
||
+ }
|
||
+
|
||
+ /*** accumulator offset ***/
|
||
+ else if (((D_REG_P (offset)) || (Q_REG_P (offset)))
|
||
+ && (A_REG_P (base))) {
|
||
+ fprintf (file, "%s,%s",
|
||
+ reg_names[REGNO (offset)], reg_names[REGNO (base)]);
|
||
+ }
|
||
+
|
||
+ else if (((D_REG_P (base)) || (Q_REG_P (base)))
|
||
+ && (A_REG_P (offset))) {
|
||
+ fprintf (file, "%s,%s",
|
||
+ reg_names[REGNO (base)], reg_names[REGNO (offset)]);
|
||
+ }
|
||
+
|
||
+ else if (GET_CODE (base) == PRE_DEC) {
|
||
+ regno = REGNO (XEXP (base, 0));
|
||
+ fputs (((last_mem_size == 1) ? ",-" : ",--"), file);
|
||
+ fprintf (file, "%s", reg_names[regno]);
|
||
+ }
|
||
+
|
||
+ else
|
||
+ abort ();
|
||
+
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ /* Set this global before calling output_addr_const() */
|
||
+ if (!indirect_flag)
|
||
+ check_direct_prefix_flag = 1;
|
||
+
|
||
+ /* When printing a SYMBOL_REF in PIC mode, do not print the leading
|
||
+ * '#' and follow it by ',pcr' to enable relative addressing. */
|
||
+ if (flag_pic && pic_ok_for_addr_p && GET_CODE (addr) == SYMBOL_REF)
|
||
+ {
|
||
+ ASM_OUTPUT_SYMBOL_REF (file, addr);
|
||
+ fputs (",pcr", file);
|
||
+ pic_ok_for_addr_p = 1;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output_addr_const (file, addr);
|
||
+ }
|
||
+
|
||
+ check_direct_prefix_flag = 0;
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ if (indirect_flag)
|
||
+ fprintf (file, "]");
|
||
+}
|
||
+
|
||
+/*-------------------------------------------------------------------
|
||
+ Update the CC Status
|
||
+---------------------------------------------------------------------
|
||
+ Set the cc_status for the results of an insn whose pattern is EXP.
|
||
+ We assume that jumps don't affect the condition codes.
|
||
+ All else, clobbers the condition codes, by assumption.
|
||
+
|
||
+ We assume that ALL add, minus, etc. instructions effect the condition
|
||
+ codes.
|
||
+-------------------------------------------------------------------*/
|
||
+void
|
||
+notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ int src_code;
|
||
+ int dst_code;
|
||
+
|
||
+ /*** recognize SET insn's ***/
|
||
+ if (GET_CODE (exp) == SET)
|
||
+ {
|
||
+ src_code = GET_CODE (SET_SRC (exp));
|
||
+ dst_code = GET_CODE (SET_DEST (exp));
|
||
+
|
||
+ /* Jumps do not alter the cc's. */
|
||
+ if (SET_DEST (exp) == pc_rtx)
|
||
+ return;
|
||
+
|
||
+ /* Moving one register into another register (tfr):
|
||
+ Doesn't alter the cc's. */
|
||
+ if (REG_P (SET_DEST (exp)) && (REG_P (SET_SRC (exp))))
|
||
+ return;
|
||
+
|
||
+ /* Moving memory into a register (load): Sets cc's. */
|
||
+ if (REG_P (SET_DEST (exp)) && src_code == MEM) {
|
||
+ cc_status.value1 = SET_SRC (exp);
|
||
+ cc_status.value2 = SET_DEST (exp);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Moving register into memory (store): Sets cc's. */
|
||
+ if (dst_code == MEM && REG_P (SET_SRC (exp))) {
|
||
+ cc_status.value1 = SET_SRC (exp);
|
||
+ cc_status.value2 = SET_DEST (exp);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Function calls clobber the cc's. */
|
||
+ else if (GET_CODE (SET_SRC (exp)) == CALL) {
|
||
+ CC_STATUS_INIT;
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Tests and compares set the cc's in predictable ways. */
|
||
+ else if (SET_DEST (exp) == cc0_rtx)
|
||
+ {
|
||
+ cc_status.flags = 0;
|
||
+ cc_status.value1 = SET_SRC (exp);
|
||
+ cc_status.value2 = SET_DEST (exp);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ else if (A_REG_P (SET_DEST (exp)))
|
||
+ {
|
||
+ CC_STATUS_INIT;
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ else
|
||
+ {
|
||
+ /* Certain instructions affect the condition codes. */
|
||
+ switch (src_code)
|
||
+ {
|
||
+ case PLUS:
|
||
+ case MINUS:
|
||
+ case NEG:
|
||
+ case ASHIFT:
|
||
+ /* These instructions set the condition codes,
|
||
+ * and may modify the V bit. */
|
||
+ cc_status.flags |= CC_NO_OVERFLOW;
|
||
+ /* FALLTHRU */
|
||
+
|
||
+ case AND:
|
||
+ case IOR:
|
||
+ case XOR:
|
||
+ case ASHIFTRT:
|
||
+ case LSHIFTRT:
|
||
+ /* These instructions set the condition codes,
|
||
+ * but cannot overflow (V=0). */
|
||
+ cc_status.value1 = SET_SRC (exp);
|
||
+ cc_status.value2 = SET_DEST (exp);
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ /* Everything else is clobbered */
|
||
+ CC_STATUS_INIT;
|
||
+ }
|
||
+ return;
|
||
+ }
|
||
+ } /* SET */
|
||
+
|
||
+ else if (GET_CODE (exp) == PARALLEL
|
||
+ && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
|
||
+ {
|
||
+ if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
|
||
+ return;
|
||
+ if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
|
||
+ {
|
||
+ CC_STATUS_INIT;
|
||
+ cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /*** default action if we haven't recognized something
|
||
+ and returned earlier ***/
|
||
+ CC_STATUS_INIT;
|
||
+}
|
||
+
|
||
+
|
||
+/** Returns nonzero if the expression EXP can be implemented using one
|
||
+ * of the 6809's single operand instructions. */
|
||
+int
|
||
+m6809_single_operand_operator (rtx exp)
|
||
+{
|
||
+ rtx op1;
|
||
+ HOST_WIDE_INT val;
|
||
+ enum rtx_code code;
|
||
+
|
||
+ debug_rtx(exp);
|
||
+
|
||
+ code = GET_CODE (exp);
|
||
+
|
||
+ /* Unary operators always qualify */
|
||
+ switch (code)
|
||
+ {
|
||
+ case NEG:
|
||
+ case NOT:
|
||
+ return 1;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Binary operators can only qualify if the second
|
||
+ * argument is a CONST_INT of certain value. */
|
||
+ op1 = XEXP (exp, 1);
|
||
+ if (GET_CODE (op1) != CONST_INT)
|
||
+ return 0;
|
||
+ val = INTVAL (op1);
|
||
+ switch (code)
|
||
+ {
|
||
+ case PLUS:
|
||
+ case MINUS:
|
||
+ if (val == -1 || val == 1)
|
||
+ return 1;
|
||
+ break;
|
||
+
|
||
+ case ASHIFT:
|
||
+ case ASHIFTRT:
|
||
+ case LSHIFTRT:
|
||
+ case ROTATE:
|
||
+ case ROTATERT:
|
||
+ if (val == 1)
|
||
+ return 1;
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+/** Return a bitarray of the hard registers which are used by a function. */
|
||
+unsigned int
|
||
+m6809_get_live_regs (void)
|
||
+{
|
||
+ unsigned int regs = 0;
|
||
+ int regno;
|
||
+
|
||
+ if (frame_pointer_needed)
|
||
+ regs |= (1 << HARD_FRAME_POINTER_REGNUM);
|
||
+
|
||
+ for (regno = HARD_X_REGNUM; regno <= HARD_U_REGNUM; regno++)
|
||
+ if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
|
||
+ regs |= (1 << regno);
|
||
+
|
||
+ return regs;
|
||
+}
|
||
+
|
||
+
|
||
+/** Return a printable version of a list of hard registers, suitable
|
||
+ * for use in a PSHx or PULx insn. */
|
||
+const char *
|
||
+m6809_get_regs_printable (unsigned int regs)
|
||
+{
|
||
+ static char list[64];
|
||
+ char *listp = list;
|
||
+ unsigned int regno;
|
||
+
|
||
+ for (regno=0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||
+ if ((regs & (1 << regno)) && !S_REGNO_P (regno))
|
||
+ listp += sprintf (listp,
|
||
+ (listp == list) ? "%s" : ",%s", reg_names[regno]);
|
||
+
|
||
+ return list;
|
||
+}
|
||
+
|
||
+
|
||
+/** Return the total number of bytes covered by a set of hard registers. */
|
||
+unsigned int
|
||
+m6809_get_regs_size (unsigned int regs)
|
||
+{
|
||
+ unsigned int regno;
|
||
+ unsigned int size = 0;
|
||
+
|
||
+ for (regno=0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||
+ {
|
||
+ /* Only count register in the given register set */
|
||
+ if (REGSET_CONTAINS_P (regno, regs))
|
||
+ {
|
||
+ /* Add 1 or 2 byte, depending on the size of the register.
|
||
+ * Since 'D' may be in both sets, check for WORD_REGSET first. */
|
||
+ if (REGSET_CONTAINS_P(regno, WORD_REGSET))
|
||
+ size += 2;
|
||
+ else if (REGSET_CONTAINS_P(regno, BYTE_REGSET))
|
||
+ size++;
|
||
+ }
|
||
+ }
|
||
+ return size;
|
||
+}
|
||
+
|
||
+
|
||
+/* Given the target of call instruction in X,
|
||
+ * return the tree node that contains the function declaration for
|
||
+ * that target.
|
||
+ *
|
||
+ * If the rtx or the tree do not appear valid for any reason,
|
||
+ * then return NULL_TREE.
|
||
+ */
|
||
+static tree call_target_decl (rtx x)
|
||
+{
|
||
+ tree decl;
|
||
+
|
||
+ /* Make sure the target is really a MEM. */
|
||
+ if (!x || !MEM_P (x))
|
||
+ return NULL_TREE;
|
||
+
|
||
+ /* Make sure the address is a SYMBOL_REF. */
|
||
+ x = XEXP (x, 0);
|
||
+ if (!x || (GET_CODE (x) != SYMBOL_REF))
|
||
+ return NULL_TREE;
|
||
+
|
||
+ /* Get the declaration of this symbol */
|
||
+ decl = SYMBOL_REF_DECL (x);
|
||
+
|
||
+ /* Make sure the declaration is really a function. */
|
||
+ if (!decl || (TREE_CODE(decl) != FUNCTION_DECL))
|
||
+ return NULL_TREE;
|
||
+
|
||
+ return decl;
|
||
+}
|
||
+
|
||
+
|
||
+/** Returns nonzero if a function, whose declaration is in DECL,
|
||
+ * was declared to have the attribute given by ATTR_NAME. */
|
||
+int
|
||
+m6809_function_has_type_attr_p (tree decl, const char *attr_name)
|
||
+{
|
||
+ tree type;
|
||
+
|
||
+ type = TREE_TYPE (decl);
|
||
+ return lookup_attribute (attr_name, TYPE_ATTRIBUTES (type)) != NULL;
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+/** Returns nonzero if the current function was declared to have the
|
||
+ * attribute given by ATTR_NAME. */
|
||
+int
|
||
+m6809_current_function_has_type_attr_p (const char *attr_name)
|
||
+{
|
||
+ return m6809_function_has_type_attr_p (current_function_decl, attr_name);
|
||
+}
|
||
+
|
||
+
|
||
+/** Return nonzero if the current function has no return value. */
|
||
+int
|
||
+m6809_current_function_is_void (void)
|
||
+{
|
||
+ return (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
|
||
+}
|
||
+
|
||
+
|
||
+/** Get the value of a declaration's 'bank', as set by the 'bank'
|
||
+ * attribute. If no bank was declared, it returns NULL by default. */
|
||
+const char *
|
||
+m6809_get_decl_bank (tree decl)
|
||
+{
|
||
+ tree attr;
|
||
+
|
||
+ /* Lookup the 'bank' attribute. If it does not exist, then
|
||
+ * return NULL */
|
||
+ attr = lookup_attribute ("bank", DECL_ATTRIBUTES (decl));
|
||
+ if (attr == NULL_TREE)
|
||
+ return NULL;
|
||
+
|
||
+ /* Make sure it has a value assigned to it */
|
||
+ attr = TREE_VALUE (attr);
|
||
+ if (attr == NULL_TREE)
|
||
+ {
|
||
+ warning (WARNING_OPT "banked function did not declare a bank number");
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ /* Return the bank name */
|
||
+ attr = TREE_VALUE (attr);
|
||
+ return TREE_STRING_POINTER (attr);
|
||
+}
|
||
+
|
||
+
|
||
+void
|
||
+m6809_declare_function_name (FILE *asm_out_file, const char *name, tree decl)
|
||
+{
|
||
+ /* Check the function declaration for special properties.
|
||
+ *
|
||
+ * If the function was declare with __attribute__((bank)), output
|
||
+ * assembler definitions to force the function to go into the named
|
||
+ * bank.
|
||
+ */
|
||
+ const char *bank_name = m6809_get_decl_bank (decl);
|
||
+ if (bank_name != NULL)
|
||
+ {
|
||
+ /* Declare __self_bank as a local assembler value that denotes
|
||
+ * which bank the current function is in. This is required only
|
||
+ * when the bank actually changes. */
|
||
+ if (strcmp (bank_name, current_bank_name))
|
||
+ {
|
||
+ fprintf (asm_out_file, "__self_bank\t.equ %s\n", bank_name);
|
||
+ strcpy (current_bank_name, bank_name);
|
||
+ }
|
||
+
|
||
+ /* Declare a global assembler value that denotes which bank the
|
||
+ * named function is in. */
|
||
+ fprintf (asm_out_file, "__%s_bank\t.gblequ %s\n", name, bank_name);
|
||
+
|
||
+ /* Force the current function into a new area */
|
||
+ fprintf (asm_out_file, "\t.bank bank_%s (FSFX=_%s)\n",
|
||
+ bank_name, bank_name);
|
||
+ fprintf (asm_out_file, "\t.area bank_%s (BANK=bank_%s)\n",
|
||
+ bank_name, bank_name);
|
||
+ }
|
||
+
|
||
+ /* Emit the label for the function's name */
|
||
+ ASM_OUTPUT_LABEL (asm_out_file, name);
|
||
+}
|
||
+
|
||
+#if 0
|
||
+/**
|
||
+ * Handle pragmas. Note that only the last branch pragma seen in the
|
||
+ * source has any affect on code generation.
|
||
+ */
|
||
+
|
||
+#define BAD_PRAGMA(msgid, arg) \
|
||
+ do { warning (WARNING_OPT msgid, arg); return -1; } while (0)
|
||
+
|
||
+static int
|
||
+pragma_parse (const char *name, tree *sect)
|
||
+{
|
||
+ tree s, x;
|
||
+
|
||
+ if (pragma_lex (&x) != CPP_OPEN_PAREN)
|
||
+ BAD_PRAGMA ("missing '(' after '#pragma %s' - ignored", name);
|
||
+
|
||
+ if (pragma_lex (&s) != CPP_STRING)
|
||
+ BAD_PRAGMA ("missing section name in '#pragma %s' - ignored", name);
|
||
+
|
||
+ if (pragma_lex (&x) != CPP_CLOSE_PAREN)
|
||
+ BAD_PRAGMA ("missing ')' for '#pragma %s' - ignored", name);
|
||
+
|
||
+ if (pragma_lex (&x) != CPP_EOF)
|
||
+ warning (WARNING_OPT "junk at end of '#pragma %s'", name);
|
||
+
|
||
+ *sect = s;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+ * Handle #pragma section.
|
||
+ * This is deprecated; code should use __attribute__(section("name"))
|
||
+ * instead.
|
||
+ */
|
||
+void pragma_section (cpp_reader *pfile ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ tree sect;
|
||
+
|
||
+ if (pragma_parse ("section", §))
|
||
+ return;
|
||
+
|
||
+ snprintf (code_section_op, 6+TREE_STRING_LENGTH (sect),
|
||
+ ".area\t%s", TREE_STRING_POINTER (sect));
|
||
+ snprintf (data_section_op, 6+TREE_STRING_LENGTH (sect),
|
||
+ ".area\t%s", TREE_STRING_POINTER (sect));
|
||
+
|
||
+ /* Mark a flag that sections have changed. Upon emitting another
|
||
+ * declaration, the new .area directive will be written. */
|
||
+ section_changed++;
|
||
+}
|
||
+#endif
|
||
+
|
||
+/**
|
||
+ * Check a `double' value for validity for a particular machine mode.
|
||
+ * Called by the CHECK_FLOAT_VALUE() machine-dependent macro.
|
||
+ */
|
||
+int
|
||
+check_float_value (enum machine_mode mode, double *d, int overflow)
|
||
+{
|
||
+ if (mode == SFmode) {
|
||
+ if (*d > 1.7014117331926443e+38) {
|
||
+ error("magnitude of constant too large for `float'");
|
||
+ *d = 1.7014117331926443e+38;
|
||
+ }
|
||
+ else if (*d < -1.7014117331926443e+38) {
|
||
+ error("magnitude of constant too large for `float'");
|
||
+ *d = -1.7014117331926443e+38;
|
||
+ }
|
||
+ else if ((*d > 0) && (*d < 2.9387358770557188e-39)) {
|
||
+ warning(WARNING_OPT "`float' constant truncated to zero");
|
||
+ *d = 0.0;
|
||
+ }
|
||
+ else if ((*d < 0) && (*d > -2.9387358770557188e-39)) {
|
||
+ warning(WARNING_OPT "`float' constant truncated to zero");
|
||
+ *d = 0.0;
|
||
+ }
|
||
+ }
|
||
+ return overflow;
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+/** Declare that the target supports named output sections. */
|
||
+bool m6809_have_named_section = (bool)1;
|
||
+
|
||
+
|
||
+/** Write to the assembler file a directive to place
|
||
+ * subsequent objects to a different section in the
|
||
+ * object file. ASxxxx uses the "area" directive for
|
||
+ * this purpose. It does not however support generalized
|
||
+ * alignment, and can only place items on an odd/even
|
||
+ * boundary. */
|
||
+void
|
||
+m6809_asm_named_section (
|
||
+ const char *name,
|
||
+ unsigned int flags ATTRIBUTE_UNUSED,
|
||
+ tree decl)
|
||
+{
|
||
+ fprintf (asm_out_file, "\t.area\t%s\n", name);
|
||
+}
|
||
+
|
||
+
|
||
+enum reg_class
|
||
+m6809_preferred_reload_class (rtx x, enum reg_class regclass)
|
||
+{
|
||
+ /* Check cases based on type code of rtx */
|
||
+ switch (GET_CODE(x))
|
||
+ {
|
||
+ /*
|
||
+ * Observation, 2015-07-19, William Astle
|
||
+ *
|
||
+ * The original comparison for range for 16 bits was wrong, adding 0x80
|
||
+ * instead of 0x8000. Replaced both 8 bit and 16 bit comparisions with
|
||
+ * a more straight forward range comparison - excessive cleverness isn't
|
||
+ * really required here.
|
||
+ */
|
||
+ case CONST_INT:
|
||
+ /* Constants that can fit into 1 byte should be
|
||
+ * loaded into a Q_REGS reg */
|
||
+ if ((INTVAL(x) >= -128 && INTVAL(x) <= 127) &&
|
||
+// if (((unsigned) (INTVAL(x) + 0x80) < 0x100) &&
|
||
+ (regclass > A_REGS))
|
||
+ return Q_REGS;
|
||
+
|
||
+ /* 16-bit constants should be loaded into A_REGS
|
||
+ * when possible. gcc may already require A_REGS
|
||
+ * or D_REGS for certain types of instructions.
|
||
+ * This case applies mostly to simple copy operations
|
||
+ * to/from memory when any register will do, but
|
||
+ * it's best to avoid using D register since it is
|
||
+ * needed for other things.
|
||
+ */
|
||
+ else if ((INTVAL(x) >= -32768 && INTVAL(x) <= 32767) &&
|
||
+// else if (((unsigned) (INTVAL(x) + 0x80) < 0x10000) &&
|
||
+ (regclass > A_REGS))
|
||
+ return A_REGS;
|
||
+ break;
|
||
+
|
||
+ case SYMBOL_REF:
|
||
+ case LABEL_REF:
|
||
+ /* Addresses should always be loaded into A_REGS */
|
||
+ if (regclass >= A_REGS)
|
||
+ return (A_REGS);
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Check cases based on mode of rtx */
|
||
+ if ((GET_MODE(x) == QImode) && (regclass != A_REGS))
|
||
+ return Q_REGS;
|
||
+
|
||
+ /* Default: return whatever class reload suggested */
|
||
+ return regclass;
|
||
+}
|
||
+
|
||
+
|
||
+/**
|
||
+ * Check a new declaration for the "section" attribute.
|
||
+ * If it exists, and the target section is "direct", then mark
|
||
+ * the declaration (in RTL) to indicate special treatment.
|
||
+ * When the variable is referenced later, we test for this flag
|
||
+ * and can emit special asm text to force the assembler to use
|
||
+ * short instructions.
|
||
+ */
|
||
+static void
|
||
+m6809_encode_section_info (tree decl, rtx rtl, int new_decl_p ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ tree attr, id;
|
||
+ const char *name;
|
||
+ const char *decl_name;
|
||
+
|
||
+ /* We only care about variable declarations, not functions */
|
||
+ if (TREE_CODE (decl) != VAR_DECL)
|
||
+ return;
|
||
+
|
||
+ /* For debugging purposes only; grab the decl's name */
|
||
+ decl_name = IDENTIFIER_POINTER (DECL_NAME (decl));
|
||
+
|
||
+ /* Give up if the decl doesn't have any RTL */
|
||
+ if (!DECL_RTL (decl))
|
||
+ return;
|
||
+
|
||
+ /* See if it has a section attribute */
|
||
+ attr = lookup_attribute ("section", DECL_ATTRIBUTES (decl));
|
||
+ if (!attr)
|
||
+ return;
|
||
+
|
||
+ /* See if the section attribute has a value */
|
||
+ id = TREE_VALUE (TREE_VALUE (attr));
|
||
+ if (!id)
|
||
+ return;
|
||
+ name = TREE_STRING_POINTER (id);
|
||
+ if (!name)
|
||
+ return;
|
||
+
|
||
+ /* See if the value is 'direct'. If so, mark it. */
|
||
+ if (!strcmp (name, "direct"))
|
||
+ SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
|
||
+}
|
||
+
|
||
+
|
||
+/**
|
||
+ * Output code to perform a complex shift, for which there is no
|
||
+ * direct support in the instruction set.
|
||
+ *
|
||
+ * shift1 is an instruction pattern for performing a 1-bit modification.
|
||
+ * This code wraps that pattern in a loop to perform the shift N times,
|
||
+ * where N is given by the address register in operands[2].
|
||
+ *
|
||
+ * To support 16-bit shifts, shift2 can also be provided: it is
|
||
+ * a second instruction to be included in the loop. 8-bit shift
|
||
+ * insns will pass NULL here.
|
||
+ *
|
||
+ * The insn length of shift1/shift2 is assumed to be 1 byte,
|
||
+ * which works in all of the cases it is needed so far.
|
||
+ */
|
||
+static void
|
||
+m6809_gen_register_shift (
|
||
+ rtx *operands,
|
||
+ const char *shift1,
|
||
+ const char *shift2 )
|
||
+{
|
||
+ char beq_pattern[32];
|
||
+ char bra_pattern[32];
|
||
+
|
||
+ int shiftlen = (shift1 && shift2) ? 2 : 1;
|
||
+ int cmplen = (REGNO (operands[2]) == HARD_X_REGNUM) ? 3 : 4;
|
||
+
|
||
+ int beq_offset = 2 + shiftlen + 2;
|
||
+ int bra_offset = shiftlen + 2 + cmplen + 2;
|
||
+
|
||
+ sprintf (beq_pattern, "beq\t.+%d", beq_offset);
|
||
+ sprintf (bra_pattern, "bra\t.-%d", bra_offset);
|
||
+
|
||
+ output_asm_insn ("pshs\t%2", operands);
|
||
+ output_asm_insn ("lea%2\t-1,%2", operands);
|
||
+ output_asm_insn ("cmp%2\t#-1", operands);
|
||
+ output_asm_insn (beq_pattern, operands);
|
||
+ if (shift1)
|
||
+ output_asm_insn (shift1, operands);
|
||
+ if (shift2)
|
||
+ output_asm_insn (shift2, operands);
|
||
+ output_asm_insn (bra_pattern, operands);
|
||
+ output_asm_insn ("puls\t%2", operands);
|
||
+}
|
||
+
|
||
+
|
||
+/** Generate RTL for the upper 8-bits of a 16-bit constant. */
|
||
+rtx
|
||
+gen_rtx_const_high (rtx r)
|
||
+{
|
||
+ unsigned char v = (INTVAL (r) >> 8) & 0xFF;
|
||
+ signed char s = (signed char)v;
|
||
+ return gen_int_mode (s, QImode);
|
||
+}
|
||
+
|
||
+
|
||
+/** Generate RTL for the lower 8-bits of a 16-bit constant. */
|
||
+rtx
|
||
+gen_rtx_const_low (rtx r)
|
||
+{
|
||
+ unsigned char v = INTVAL (r) & 0xFF;
|
||
+ signed char s = (signed char)v;
|
||
+ return gen_int_mode (s, QImode);
|
||
+}
|
||
+
|
||
+
|
||
+/** Generate RTL to allocate/free bytes on the stack.
|
||
+ * CODE is given as MINUS when allocating and PLUS when freeing,
|
||
+ * to match the semantics of a downward-growing stack. SIZE
|
||
+ * is always given as a positive integer.
|
||
+ */
|
||
+static rtx
|
||
+gen_rtx_stack_adjust (enum rtx_code code, int size)
|
||
+{
|
||
+ if (size <= 0)
|
||
+ return NULL_RTX;
|
||
+
|
||
+ if (code == MINUS)
|
||
+ size = -size;
|
||
+
|
||
+ return gen_rtx_SET (Pmode, stack_pointer_rtx,
|
||
+ gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
||
+ gen_int_mode (size, HImode)));
|
||
+}
|
||
+
|
||
+
|
||
+/** Generate RTL to push/pop a set of registers. */
|
||
+rtx
|
||
+gen_rtx_register_pushpop (int op, int regs)
|
||
+{
|
||
+ rtx nregs = gen_int_mode (regs, QImode);
|
||
+
|
||
+ if (op == UNSPEC_PUSH_RS)
|
||
+ return gen_register_push (nregs);
|
||
+ else
|
||
+ return gen_register_pop (nregs);
|
||
+}
|
||
+
|
||
+
|
||
+/* Given a register set REGS, where the bit positions correspond to
|
||
+ * hard register numbers, return another bitmask that represents the
|
||
+ * order in which those registers would be pushed/popped.
|
||
+ * Registers that are pushed first have higher bit positions.
|
||
+ * The pop order is just the reverse bitmask.
|
||
+ * These values are the same as the bitmasks actually used in the
|
||
+ * machine instructions. */
|
||
+static unsigned int
|
||
+register_push_order (int regs)
|
||
+{
|
||
+ unsigned int order = 0;
|
||
+
|
||
+ if (REGSET_CONTAINS_P (HARD_PC_REGNUM, regs))
|
||
+ order |= 0x80;
|
||
+ if (REGSET_CONTAINS_P (HARD_U_REGNUM, regs))
|
||
+ order |= 0x40;
|
||
+ if (REGSET_CONTAINS_P (HARD_Y_REGNUM, regs))
|
||
+ order |= 0x20;
|
||
+ if (REGSET_CONTAINS_P (HARD_X_REGNUM, regs))
|
||
+ order |= 0x10;
|
||
+ if (REGSET_CONTAINS_P (HARD_DP_REGNUM, regs))
|
||
+ order |= 0x8;
|
||
+ if (REGSET_CONTAINS_P (HARD_B_REGNUM, regs))
|
||
+ order |= 0x4;
|
||
+ if (REGSET_CONTAINS_P (HARD_A_REGNUM, regs))
|
||
+ order |= 0x2;
|
||
+ if (REGSET_CONTAINS_P (HARD_CC_REGNUM, regs))
|
||
+ order |= 0x1;
|
||
+
|
||
+ if (REGSET_CONTAINS_P (HARD_D_REGNUM, regs))
|
||
+ order |= (0x4 | 0x2);
|
||
+ return order;
|
||
+}
|
||
+
|
||
+
|
||
+/* Returns nonzero if two consecutive push or pop instructions,
|
||
+ * as determined by the OP, can be merged into a single instruction.
|
||
+ * The first instruction in the sequence pushes/pops REGS1; the
|
||
+ * second applies to REGS2.
|
||
+ *
|
||
+ * If true, the resulting instruction can use (regs1 | regs2)
|
||
+ * safely.
|
||
+ */
|
||
+int
|
||
+m6809_can_merge_pushpop_p (int op, int regs1, int regs2)
|
||
+{
|
||
+ /* Register sets must not overlap */
|
||
+ if (regs1 & regs2)
|
||
+ return 0;
|
||
+
|
||
+ if (op == UNSPEC_PUSH_RS)
|
||
+ return (register_push_order (regs1) > register_push_order (regs2));
|
||
+ else if (op == UNSPEC_POP_RS)
|
||
+ return (register_push_order (regs1) < register_push_order (regs2));
|
||
+ else
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+/** Emit instructions for making a library call.
|
||
+ * MODE is the mode of the operation.
|
||
+ * NAME is the library function name.
|
||
+ * OPERANDS is the rtx array provided by the recognizer.
|
||
+ * COUNT is the number of input operands to the call, and
|
||
+ * should be 1 for a unary op or 2 for a binary op.
|
||
+ */
|
||
+void
|
||
+emit_libcall_insns (enum machine_mode mode,
|
||
+ const char *name,
|
||
+ rtx *operands,
|
||
+ int count)
|
||
+{
|
||
+ /* Generate an rtx for the call target. */
|
||
+ rtx symbol = gen_rtx_SYMBOL_REF (Pmode, name);
|
||
+
|
||
+ /* Emit the library call. Slightly different based
|
||
+ on the number of operands */
|
||
+ if (count == 2)
|
||
+ emit_library_call (symbol, LCT_NORMAL, mode,
|
||
+ 2, operands[1], mode, operands[2], mode);
|
||
+ else
|
||
+ emit_library_call (symbol, LCT_NORMAL, mode,
|
||
+ 1, operands[1], mode);
|
||
+
|
||
+ /* The library call is expected to put its result
|
||
+ in LIBCALL_VALUE, so need to copy it into the destination. */
|
||
+ emit_move_insn (operands[0], LIBCALL_VALUE(mode));
|
||
+}
|
||
+
|
||
+
|
||
+/**
|
||
+ * A small helper function that writes out a single branch instruction.
|
||
+ * OPCODE is the short name, e.g. "ble".
|
||
+ * OPERANDS has the rtx for the target label.
|
||
+ * LONG_P is nonzero if we are emitting a long branch, and need to
|
||
+ * prepend an 'l' to the opcode name.
|
||
+ */
|
||
+void output_branch_insn1 (const char *opcode, rtx *operands, int long_p)
|
||
+{
|
||
+ char pattern[64];
|
||
+ sprintf (pattern, "%s%s\t%%l0", long_p ? "l" : "", opcode);
|
||
+ output_asm_insn (pattern, operands);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * Output a branch/conditional branch insn of the proper
|
||
+ * length. code identifies the particular branch insn.
|
||
+ * operands holds the branch target in operands[0].
|
||
+ * length says what the size of this insn should be.
|
||
+ * Based on the length, we know whether it should be a
|
||
+ * short (8-bit) or long (16-bit) branch.
|
||
+ */
|
||
+const char *
|
||
+output_branch_insn (enum rtx_code code, rtx *operands, int length)
|
||
+{
|
||
+ int shortform;
|
||
+
|
||
+ /* Decide whether or not to use the long or short form.
|
||
+ * Calculate automatically based on insn lengths. */
|
||
+ shortform = ((length > 2) ? 0 : 1);
|
||
+
|
||
+ /* Determine the proper opcode.
|
||
+ * Use the short (2-byte) opcode if the target is within
|
||
+ * reach. Otherwise, use jmp (3-byte opcode), unless
|
||
+ * compiling with -fpic, in which case we'll need to use
|
||
+ * lbra (4-byte opcode).
|
||
+ */
|
||
+ switch (code)
|
||
+ {
|
||
+ case LABEL_REF:
|
||
+ if (shortform)
|
||
+ output_branch_insn1 ("bra", operands, 0);
|
||
+ else if (flag_pic)
|
||
+ output_branch_insn1 ("bra", operands, 1);
|
||
+ else
|
||
+ output_branch_insn1 ("jmp", operands, 0);
|
||
+ break;
|
||
+ case EQ:
|
||
+ output_branch_insn1 ("beq", operands, !shortform);
|
||
+ break;
|
||
+ case NE:
|
||
+ output_branch_insn1 ("bne", operands, !shortform);
|
||
+ break;
|
||
+ case GT:
|
||
+ output_branch_insn1 ("bgt", operands, !shortform);
|
||
+ break;
|
||
+ case GTU:
|
||
+ output_branch_insn1 ("bhi", operands, !shortform);
|
||
+ break;
|
||
+ case LT:
|
||
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
|
||
+ {
|
||
+ output_branch_insn1 ("bmi", operands, !shortform);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output_branch_insn1 ("blt", operands, !shortform);
|
||
+ }
|
||
+ break;
|
||
+ case LTU:
|
||
+ output_branch_insn1 ("blo", operands, !shortform);
|
||
+ break;
|
||
+ case GE:
|
||
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
|
||
+ {
|
||
+ output_branch_insn1 ("bpl", operands, !shortform);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output_branch_insn1 ("bge", operands, !shortform);
|
||
+ }
|
||
+ break;
|
||
+ case GEU:
|
||
+ output_branch_insn1 ("bhs", operands, !shortform);
|
||
+ break;
|
||
+ case LE:
|
||
+ if (cc_prev_status.flags & CC_NO_OVERFLOW)
|
||
+ {
|
||
+ output_branch_insn1 ("bmi", operands, !shortform);
|
||
+ output_branch_insn1 ("beq", operands, !shortform);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output_branch_insn1 ("ble", operands, !shortform);
|
||
+ }
|
||
+ break;
|
||
+ case LEU:
|
||
+ output_branch_insn1 ("bls", operands, !shortform);
|
||
+ break;
|
||
+ default:
|
||
+ abort();
|
||
+ break;
|
||
+ }
|
||
+ return "";
|
||
+}
|
||
+
|
||
+
|
||
+/** Returns the "cost" of an RTL expression.
|
||
+ * In general, the expression "COSTS_N_INSNS(1)" is used to represent
|
||
+ * the cost of a fast 8-bit arithmetic instruction that operates on
|
||
+ * a reg/mem or a reg/immed. Other costs are relative to this.
|
||
+ *
|
||
+ * Notes:
|
||
+ * - The cost of a REG is always zero; this cannot be changed.
|
||
+ *
|
||
+ * - On the 6809, instructions on two registers will nearly always take
|
||
+ * longer than those that operate on a register and a constant/memory,
|
||
+ * because of the way the instruction set is structured.
|
||
+ *
|
||
+ * TODO: multiply HImode by 2 should be done via shifts, instead of add.
|
||
+ */
|
||
+static bool
|
||
+m6809_rtx_costs (rtx X, int code, int outer_code ATTRIBUTE_UNUSED,
|
||
+ int *total, bool speed)
|
||
+{
|
||
+ int has_const_arg = 0;
|
||
+ HOST_WIDE_INT const_arg;
|
||
+ enum machine_mode mode;
|
||
+ int nargs = 1;
|
||
+ rtx op0, op1;
|
||
+
|
||
+ /* Data RTXs return a value between 0-3, depending on complexity.
|
||
+ All of these are less than COSTS_N_INSNS(1). */
|
||
+ switch (code)
|
||
+ {
|
||
+ case CC0:
|
||
+ case PC:
|
||
+ *total = 0;
|
||
+ return true;
|
||
+
|
||
+ case CONST_INT:
|
||
+ if (X == const0_rtx)
|
||
+ {
|
||
+ *total = 0;
|
||
+ return true;
|
||
+ }
|
||
+ else if ((unsigned) INTVAL (X) < 077)
|
||
+ {
|
||
+ *total = 1;
|
||
+ return true;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ *total = 2;
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ case LABEL_REF: case CONST:
|
||
+ *total = 2;
|
||
+ return true;
|
||
+
|
||
+ case SYMBOL_REF:
|
||
+ /* References to memory are made cheaper if they have
|
||
+ * the 'direct' mode attribute set */
|
||
+ *total = (SYMBOL_REF_FLAG (X)) ? 1 : 2;
|
||
+ return true;
|
||
+
|
||
+ case MEM:
|
||
+ /* See what form of address was given */
|
||
+ X = XEXP (X, 0);
|
||
+ switch (GET_CODE (X))
|
||
+ {
|
||
+ case SYMBOL_REF:
|
||
+ *total = (SYMBOL_REF_FLAG (X)) ? 1 : 2;
|
||
+ break;
|
||
+
|
||
+ case CONST_INT:
|
||
+ *total = 2;
|
||
+ break;
|
||
+
|
||
+ case MEM:
|
||
+ *total = COSTS_N_INSNS (1) + 2;
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+ return true;
|
||
+
|
||
+ case CONST_DOUBLE:
|
||
+ /* TODO : not sure about this value. */
|
||
+ *total = 3;
|
||
+ return true;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Decode the rtx */
|
||
+ mode = GET_MODE (X);
|
||
+ op0 = XEXP (X, 0);
|
||
+ op1 = XEXP (X, 1);
|
||
+
|
||
+ /* We don't implement anything in SImode or greater. */
|
||
+ if (GET_MODE_SIZE (mode) >= GET_MODE_SIZE (SImode))
|
||
+ {
|
||
+ *total = COSTS_N_INSNS (100);
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ /* Figure out if there is a constant argument, and its value. */
|
||
+ if (GET_RTX_CLASS (code) == RTX_BIN_ARITH
|
||
+ || GET_RTX_CLASS (code) == RTX_COMM_ARITH)
|
||
+ {
|
||
+ nargs = 2;
|
||
+ if (GET_CODE (op1) == CONST_INT)
|
||
+ {
|
||
+ has_const_arg = 1;
|
||
+ const_arg = INTVAL (op1);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Penalize a reg/reg operation by adding MEMORY_MOVE_COST,
|
||
+ * Ignore soft registers, since these are really in memory.
|
||
+ *
|
||
+ * TODO: penalize HImode reg/reg for most operations, except maybe
|
||
+ * additions since index registers allow for that.
|
||
+ *
|
||
+ * TODO: shifts by constant N do not always require N instructions;
|
||
+ * some of this can be done cheaper. The number of actual insns can be
|
||
+ * predicted well.
|
||
+ */
|
||
+ if (nargs == 2 && REAL_REG_P (op0) && REAL_REG_P (op1))
|
||
+ {
|
||
+ *total = MEMORY_MOVE_COST (mode, Q_REGS, 0);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ *total = 0;
|
||
+ }
|
||
+
|
||
+ /* Operator RTXs are counted as COSTS_N_INSNS(N), where N is
|
||
+ the estimated number of actual machine instructions needed to
|
||
+ perform the computation. Some small adjustments are made since
|
||
+ some "instructions" are more complex than others. */
|
||
+ switch (code)
|
||
+ {
|
||
+ case PLUS: case MINUS: case COMPARE:
|
||
+ /* 6809 handles these natively in QImode, and in HImode as long
|
||
+ * as operand 1 is constant. */
|
||
+ if (mode == QImode || (mode == HImode && has_const_arg))
|
||
+ *total += COSTS_N_INSNS (1);
|
||
+ else
|
||
+ *total += COSTS_N_INSNS (GET_MODE_SIZE (mode));
|
||
+
|
||
+ /* -1, 0, and 1 can be done using inherent instructions
|
||
+ * for PLUS and MINUS in QImode, so don't add extra cost. */
|
||
+ if (has_const_arg
|
||
+ && (mode == QImode || mode == HImode)
|
||
+ && (const_arg == -1 || const_arg == 0 || const_arg == 1)
|
||
+ && (code == PLUS || code == MINUS))
|
||
+ {
|
||
+ return true;
|
||
+ }
|
||
+ break;
|
||
+
|
||
+ case AND: case IOR: case XOR:
|
||
+ case NEG: case NOT:
|
||
+ /* 6809 handles these natively in QImode, but requires
|
||
+ * splitting in HImode. Treat these as 2 insns. */
|
||
+ *total += COSTS_N_INSNS (1) * GET_MODE_SIZE (mode);
|
||
+ break;
|
||
+
|
||
+ case ASHIFT: case ASHIFTRT: case LSHIFTRT:
|
||
+ case ROTATE: case ROTATERT:
|
||
+ /* 6809 can do shift/rotates of a QImode by a constant in
|
||
+ * 1 insn times the shift count, or in HImode by a constant
|
||
+ * by splitting to 2 insns.
|
||
+ *
|
||
+ * Shift by a nonconstant will take significantly longer
|
||
+ * than any of these. */
|
||
+ if (has_const_arg)
|
||
+ {
|
||
+ const_arg %= (GET_MODE_SIZE (mode) * 8);
|
||
+ if (const_arg == 0)
|
||
+ {
|
||
+ *total += COSTS_N_INSNS(1);
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ /* HImode shifts greater than 8 get optimized due
|
||
+ * to register transfer from b to a; this cuts down the
|
||
+ * cost. */
|
||
+ if (const_arg >= 8)
|
||
+ {
|
||
+ *total += COSTS_N_INSNS (1);
|
||
+ const_arg -= 8;
|
||
+ }
|
||
+
|
||
+ /* The computed cost is 'const_arg' 1-bit shifts, doubled
|
||
+ if in HImode, minus the cost of the constant itself which
|
||
+ will be added in later but really shouldn't be. */
|
||
+ *total += COSTS_N_INSNS (const_arg) * GET_MODE_SIZE (mode) - 1;
|
||
+ return true;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ /* It may take up to 7 iterations of about 6-7 real
|
||
+ * instructions, so make this expensive. */
|
||
+ *total += COSTS_N_INSNS (50);
|
||
+ }
|
||
+ break;
|
||
+
|
||
+ case MULT:
|
||
+ {
|
||
+ /* Multiply is cheap when both arguments are 8-bits. They
|
||
+ could be QImode, or QImode widened to HImode, or a constant
|
||
+ that fits into 8-bits. As long as both operands qualify,
|
||
+ we can use a single mul instruction.
|
||
+
|
||
+ Assume that fast multiply can be used, and change this if we find
|
||
+ differently... */
|
||
+ int ok_for_qihi3 = 1;
|
||
+
|
||
+ /* Check the first operand */
|
||
+ switch (GET_MODE (op0))
|
||
+ {
|
||
+ case QImode:
|
||
+ break;
|
||
+ case HImode:
|
||
+ if (GET_CODE (op0) != SIGN_EXTEND && GET_CODE (op0) != ZERO_EXTEND)
|
||
+ ok_for_qihi3 = 0;
|
||
+ break;
|
||
+ default:
|
||
+ ok_for_qihi3 = 0;
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Likewise, check the second operand. This is where constants may appear. */
|
||
+ switch (GET_MODE (op1))
|
||
+ {
|
||
+ case QImode:
|
||
+ break;
|
||
+ case HImode:
|
||
+ if (GET_CODE (op1) != SIGN_EXTEND && GET_CODE (op1) != ZERO_EXTEND)
|
||
+ ok_for_qihi3 = 0;
|
||
+ break;
|
||
+ case VOIDmode:
|
||
+ if (!CONST_OK_FOR_LETTER_P (const_arg, 'K'))
|
||
+ ok_for_qihi3 = 0;
|
||
+ break;
|
||
+ default:
|
||
+ ok_for_qihi3 = 0;
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Fast multiply takes about 4 times as many cycles as a normal
|
||
+ arithmetic operation. Otherwise, it will take an expensive libcall. */
|
||
+ if (ok_for_qihi3)
|
||
+ *total += COSTS_N_INSNS (4);
|
||
+ else
|
||
+ *total = COSTS_N_INSNS (50);
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ case DIV: case UDIV: case MOD: case UMOD:
|
||
+ /* These all require more expensive libcalls. */
|
||
+ *total += COSTS_N_INSNS (100);
|
||
+ break;
|
||
+
|
||
+ /* TODO : TRUNCATE, SIGN_EXTEND, and ZERO_EXTEND */
|
||
+
|
||
+ /* These can normally be done with autoincrement, etc., so
|
||
+ * don't charge for them. */
|
||
+ case PRE_DEC:
|
||
+ case PRE_INC:
|
||
+ case POST_DEC:
|
||
+ case POST_INC:
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Always return false, and let the caller gather the costs
|
||
+ * of the operands */
|
||
+ return false;
|
||
+}
|
||
+
|
||
+
|
||
+static tree
|
||
+m6809_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)
|
||
+ {
|
||
+ warning (WARNING_OPT "'%s' only valid for functions",
|
||
+ IDENTIFIER_POINTER (name));
|
||
+ *no_add_attrs = TRUE;
|
||
+ }
|
||
+
|
||
+ return NULL_TREE;
|
||
+}
|
||
+
|
||
+
|
||
+static tree
|
||
+m6809_handle_data_type_attribute (tree *node ATTRIBUTE_UNUSED,
|
||
+ tree name ATTRIBUTE_UNUSED,
|
||
+ tree args ATTRIBUTE_UNUSED,
|
||
+ int flags ATTRIBUTE_UNUSED,
|
||
+ bool *no_add_attrs ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ return NULL_TREE;
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+static tree
|
||
+m6809_handle_default_attribute (tree *node ATTRIBUTE_UNUSED,
|
||
+ tree name ATTRIBUTE_UNUSED,
|
||
+ tree args ATTRIBUTE_UNUSED,
|
||
+ int flags ATTRIBUTE_UNUSED,
|
||
+ bool *no_add_attrs ATTRIBUTE_UNUSED )
|
||
+{
|
||
+ return NULL_TREE;
|
||
+}
|
||
+
|
||
+
|
||
+/* Table of valid machine attributes */
|
||
+const struct attribute_spec m6809_attribute_table[] = { /*
|
||
+{ name, min, max, decl, type, fntype, handler } */
|
||
+{ "interrupt", 0, 0, false, true, true, m6809_handle_fntype_attribute },
|
||
+{ "naked", 0, 0, false, true, true, m6809_handle_fntype_attribute },
|
||
+{ "far", 0, 1, false, true, true, m6809_handle_fntype_attribute },
|
||
+{ "bank", 0, 1, true, false, false, m6809_handle_default_attribute },
|
||
+{ "boolean", 0, 0, false, true, false, m6809_handle_data_type_attribute },
|
||
+{ NULL, 0, 0, false, true, false, NULL },
|
||
+};
|
||
+
|
||
+
|
||
+/** Initialize builtin routines for the 6809. */
|
||
+void
|
||
+m6809_init_builtins (void)
|
||
+{
|
||
+ /* Create type trees for each function signature required.
|
||
+ *
|
||
+ * void_ftype_void = void f(void)
|
||
+ * void_ftype_uchar = void f(unsigned char)
|
||
+ * uchar_ftype_uchar2 = unsigned char f (unsigned char, unsigned char)
|
||
+ */
|
||
+ tree void_ftype_void =
|
||
+ build_function_type (void_type_node, void_list_node);
|
||
+
|
||
+ tree void_ftype_uchar =
|
||
+ build_function_type (void_type_node,
|
||
+ tree_cons (NULL_TREE, unsigned_char_type_node, void_list_node));
|
||
+
|
||
+ tree uchar_ftype_uchar2 =
|
||
+ build_function_type (unsigned_char_type_node,
|
||
+ tree_cons (NULL_TREE, unsigned_char_type_node,
|
||
+ tree_cons (NULL_TREE, unsigned_char_type_node, void_list_node)));
|
||
+
|
||
+ /* Register each builtin function. */
|
||
+ add_builtin_function ("__builtin_swi", void_ftype_void,
|
||
+ M6809_SWI, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_swi2", void_ftype_void,
|
||
+ M6809_SWI2, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_swi3", void_ftype_void,
|
||
+ M6809_SWI3, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_cwai", void_ftype_uchar,
|
||
+ M6809_CWAI, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_sync", void_ftype_void,
|
||
+ M6809_SYNC, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_nop", void_ftype_void,
|
||
+ M6809_NOP, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_blockage", void_ftype_void,
|
||
+ M6809_BLOCKAGE, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_add_decimal", uchar_ftype_uchar2,
|
||
+ M6809_ADD_DECIMAL, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_add_carry", uchar_ftype_uchar2,
|
||
+ M6809_ADD_CARRY, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+
|
||
+ add_builtin_function ("__builtin_sub_carry", uchar_ftype_uchar2,
|
||
+ M6809_SUB_CARRY, BUILT_IN_MD, NULL, NULL_TREE);
|
||
+}
|
||
+
|
||
+
|
||
+/** Used by m6809_expand_builtin, given a tree ARGLIST which
|
||
+ * refers to the operands of a builtin call, return an rtx
|
||
+ * that represents the nth operand, as denoted by OPNUM, which
|
||
+ * is a zero-based integer. MODE gives the expected mode
|
||
+ * of the operand.
|
||
+ *
|
||
+ * This rtx is suitable for use in the emitted RTL for the
|
||
+ * builtin instruction. */
|
||
+rtx
|
||
+m6809_builtin_operand (tree arglist, enum machine_mode mode, int opnum)
|
||
+{
|
||
+ tree arg;
|
||
+ rtx r;
|
||
+
|
||
+ arg = CALL_EXPR_ARG (arglist, opnum);
|
||
+
|
||
+ /* Convert the tree to RTL */
|
||
+ r = expand_expr (arg, NULL_RTX, mode, EXPAND_NORMAL);
|
||
+ if (r == NULL_RTX)
|
||
+ return NULL_RTX;
|
||
+ return r;
|
||
+}
|
||
+
|
||
+
|
||
+/** Expand a builtin that was registered in init_builtins into
|
||
+ * RTL. */
|
||
+rtx
|
||
+m6809_expand_builtin (tree exp,
|
||
+ rtx target,
|
||
+ rtx subtarget ATTRIBUTE_UNUSED,
|
||
+ enum machine_mode mode ATTRIBUTE_UNUSED,
|
||
+ int ignore ATTRIBUTE_UNUSED )
|
||
+{
|
||
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
|
||
+ tree arglist = exp;
|
||
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
|
||
+ rtx r0, r1;
|
||
+
|
||
+ switch (fcode)
|
||
+ {
|
||
+ case M6809_SWI:
|
||
+ r0 = gen_rtx_CONST_INT (VOIDmode, 1);
|
||
+ emit_insn (target = gen_m6809_swi (r0));
|
||
+ return target;
|
||
+
|
||
+ case M6809_SWI2:
|
||
+ r0 = gen_rtx_CONST_INT (VOIDmode, 2);
|
||
+ emit_insn (target = gen_m6809_swi (r0));
|
||
+ return target;
|
||
+
|
||
+ case M6809_SWI3:
|
||
+ r0 = gen_rtx_CONST_INT (VOIDmode, 3);
|
||
+ emit_insn (target = gen_m6809_swi (r0));
|
||
+ return target;
|
||
+
|
||
+ case M6809_CWAI:
|
||
+ r0 = m6809_builtin_operand (arglist, QImode, 0);
|
||
+ emit_insn (target = gen_m6809_cwai (r0));
|
||
+ return target;
|
||
+
|
||
+ case M6809_SYNC:
|
||
+ emit_insn (target = gen_m6809_sync ());
|
||
+ return target;
|
||
+
|
||
+ case M6809_ADD_CARRY:
|
||
+ r0 = m6809_builtin_operand (arglist, QImode, 0);
|
||
+ r1 = m6809_builtin_operand (arglist, QImode, 1);
|
||
+ if (!target)
|
||
+ target = gen_reg_rtx (QImode);
|
||
+ emit_insn (gen_addqi3_carry (target, r0, r1));
|
||
+ return target;
|
||
+
|
||
+ case M6809_SUB_CARRY:
|
||
+ r0 = m6809_builtin_operand (arglist, QImode, 0);
|
||
+ r1 = m6809_builtin_operand (arglist, QImode, 1);
|
||
+ if (!target)
|
||
+ target = gen_reg_rtx (QImode);
|
||
+ emit_insn (gen_subqi3_carry (target, r0, r1));
|
||
+ return target;
|
||
+
|
||
+ case M6809_NOP:
|
||
+ emit_insn (target = gen_nop ());
|
||
+ return target;
|
||
+
|
||
+ case M6809_BLOCKAGE:
|
||
+ emit_insn (target = gen_blockage ());
|
||
+ return target;
|
||
+
|
||
+ case M6809_ADD_DECIMAL:
|
||
+ r0 = m6809_builtin_operand (arglist, QImode, 0);
|
||
+ r1 = m6809_builtin_operand (arglist, QImode, 1);
|
||
+ if (!target)
|
||
+ target = gen_reg_rtx (QImode);
|
||
+ emit_insn (gen_addqi3_decimal (target, r0, r1));
|
||
+ return target;
|
||
+
|
||
+ default:
|
||
+ warning (WARNING_OPT "unknown builtin expansion ignored");
|
||
+ return NULL_RTX;
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+/* Returns nonzero if 'x' represents a function that was declared
|
||
+ * as __noreturn__. */
|
||
+int
|
||
+noreturn_functionp (rtx x)
|
||
+{
|
||
+ tree decl = call_target_decl (x);
|
||
+
|
||
+ if (decl == NULL_TREE)
|
||
+ return 0;
|
||
+ else
|
||
+ return TREE_THIS_VOLATILE (decl);
|
||
+}
|
||
+
|
||
+
|
||
+const char *
|
||
+far_function_type_p (tree type)
|
||
+{
|
||
+ tree attr;
|
||
+ const char *page;
|
||
+
|
||
+ /* Return whether or not this decl has the far attribute */
|
||
+ attr = lookup_attribute ("far", TYPE_ATTRIBUTES (type));
|
||
+ if (attr == NULL_TREE)
|
||
+ return NULL;
|
||
+
|
||
+ /* If it is far, check for a value */
|
||
+ attr = TREE_VALUE (attr);
|
||
+ if (attr == NULL_TREE)
|
||
+ {
|
||
+ warning (WARNING_OPT "far code page not specified, using local value");
|
||
+ return far_code_page;
|
||
+ }
|
||
+
|
||
+ /* We have a TREE_LIST of attribute values, get the first one.
|
||
+ * It should be an INTEGER_CST. */
|
||
+ attr = TREE_VALUE (attr);
|
||
+ page = TREE_STRING_POINTER (attr);
|
||
+ return page;
|
||
+}
|
||
+
|
||
+
|
||
+/* For a far function, returns the identifier that states which page
|
||
+ * it resides in. Otherwise, returns NULL for ordinary functions. */
|
||
+const char *
|
||
+far_functionp (rtx x)
|
||
+{
|
||
+ tree decl, decl_type;
|
||
+ const char *page;
|
||
+
|
||
+ /* Find the FUNCTION_DECL corresponding to the rtx being called. */
|
||
+ decl = call_target_decl (x);
|
||
+ if (decl == NULL_TREE)
|
||
+ return NULL;
|
||
+
|
||
+ /* See if the function has the new 'banked' attribute. These
|
||
+ * are numeric instead of text */
|
||
+ page = m6809_get_decl_bank (decl);
|
||
+ if (page)
|
||
+ return page;
|
||
+
|
||
+ /* No, lookup the type of the function and see if the type
|
||
+ * specifies far or not. */
|
||
+ decl_type = TREE_TYPE (decl);
|
||
+ if (decl_type == NULL_TREE)
|
||
+ return NULL;
|
||
+ return far_function_type_p (decl_type);
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+/** Outputs the assembly language for a far call. */
|
||
+void
|
||
+output_far_call_insn (rtx *operands, int has_return)
|
||
+{
|
||
+ static char page_data[64];
|
||
+ const char *called_page;
|
||
+
|
||
+ /* The logic is the same for functions whether or not there
|
||
+ * is a return value. Skip over the return value in this
|
||
+ * case, so that the call location is always operands[0]. */
|
||
+ if (has_return)
|
||
+ operands++;
|
||
+
|
||
+ /* Get the name of the page being called */
|
||
+ called_page = far_functionp (operands[0]);
|
||
+
|
||
+#if 0 /* TODO : broken logic */
|
||
+ /* See if the called page name is a 'bank' */
|
||
+ if (isdigit (*called_page))
|
||
+ {
|
||
+ /* New style banking */
|
||
+ if (!strcmp (called_page, current_bank_name))
|
||
+ {
|
||
+ /* Same page */
|
||
+ output_asm_insn ("jsr\t%0", operands);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ /* Different page */
|
||
+ output_asm_insn ("jsr\t__far_call_handler\t;new style", operands);
|
||
+ output_asm_insn ("\t.dw\t%0", operands);
|
||
+ sprintf (page_data, "\t.db\t%s", called_page);
|
||
+ output_asm_insn (page_data, operands);
|
||
+ }
|
||
+ return;
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ /* Are we calling a different page than we are running in? */
|
||
+ if (!strcmp (called_page, far_code_page))
|
||
+ {
|
||
+ /* Same page : no need to execute a far call */
|
||
+ if (flag_pic)
|
||
+ output_asm_insn ("lbsr\t%C0", operands);
|
||
+ else
|
||
+ output_asm_insn ("jsr\t%0", operands);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ /* Different page : need to emit far call thunk */
|
||
+
|
||
+ /* First output a call to the thunk for making far calls. */
|
||
+ if (flag_pic)
|
||
+ output_asm_insn ("lbsr\t__far_call_handler", operands);
|
||
+ else
|
||
+ output_asm_insn ("jsr\t__far_call_handler\t;old style", operands);
|
||
+
|
||
+ /* Now output the name of the call site */
|
||
+ output_asm_insn ("\t.dw\t%C0", operands);
|
||
+
|
||
+ /* Finally output the page number */
|
||
+ sprintf (page_data, "\t.db\t%s", far_functionp (operands[0]));
|
||
+ output_asm_insn (page_data, operands);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+int
|
||
+m6809_init_cumulative_args (CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED,
|
||
+ tree fntype,
|
||
+ rtx libname ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ cum = 0;
|
||
+
|
||
+ /* For far functions, the current implementation does not allow for
|
||
+ * stack parameters. So note whenever the called function is far
|
||
+ * and in a different page than the current one; such a function
|
||
+ * should give an error if a stack parameter is generated. */
|
||
+ if (fntype)
|
||
+ {
|
||
+ const char *called_page = far_function_type_p (fntype);
|
||
+ if (called_page && strcmp (called_page, far_code_page) && !TARGET_FAR_STACK_PARAM)
|
||
+ cum |= CUM_STACK_INVALID;
|
||
+ }
|
||
+
|
||
+ if (fntype && TYPE_ARG_TYPES (fntype) != 0 &&
|
||
+ (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
|
||
+ {
|
||
+ /* has variable arguments, cannot use registers */
|
||
+ cum |= (CUM_X_MASK | CUM_B_MASK | CUM_STACK_ONLY);
|
||
+ }
|
||
+
|
||
+ if (m6809_abi_version == M6809_ABI_VERSION_STACK)
|
||
+ {
|
||
+ /* cannot use registers ; only use the stack */
|
||
+ cum |= (CUM_STACK_ONLY | CUM_X_MASK | CUM_B_MASK);
|
||
+ }
|
||
+
|
||
+ return cum;
|
||
+}
|
||
+
|
||
+
|
||
+rtx
|
||
+m6809_function_arg_on_stack (CUMULATIVE_ARGS *cump)
|
||
+{
|
||
+ if (*cump & CUM_STACK_INVALID)
|
||
+ {
|
||
+ *cump &= ~CUM_STACK_INVALID;
|
||
+ error ("far function needs stack, will not work");
|
||
+ }
|
||
+ return NULL_RTX;
|
||
+}
|
||
+
|
||
+void m6809_asm_trampoline_template(FILE *f)
|
||
+{
|
||
+ fprintf(f, "ldy #0000\n");
|
||
+ fprintf(f, "jmp 0x0000\n");
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Trampoline output:
|
||
+ *
|
||
+ * ldu #&cxt 4 bytes --LDY- ?? ??
|
||
+ * jmp fnaddr 3 bytes JMP ?? ??
|
||
+ */
|
||
+void
|
||
+m6809_initialize_trampoline (rtx tramp, tree fndecl, rtx cxt)
|
||
+{
|
||
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
|
||
+ /* TODO - optimize by generating the entire trampoline code here,
|
||
+ * and removing the template altogether, since there are only two
|
||
+ * bytes there that matter. */
|
||
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
|
||
+ emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 5)), fnaddr);
|
||
+}
|
||
+
|
||
+
|
||
+/** Echo the version of the compiler and the name of the source file
|
||
+ * at the beginning of each assembler output file. asm_out_file
|
||
+ * is a global FILE * pointing to the output stream. */
|
||
+void
|
||
+m6809_asm_file_start (void)
|
||
+{
|
||
+ const char *module_name;
|
||
+
|
||
+ fprintf (asm_out_file, "\n;;; gcc for m6809 : %s %s\n",
|
||
+ __DATE__, __TIME__);
|
||
+ fprintf (asm_out_file, ";;; %s\n", version_string);
|
||
+
|
||
+ fprintf (asm_out_file, ";;; ABI version %d\n", m6809_abi_version);
|
||
+ fprintf (asm_out_file, ";;; %s\n",
|
||
+ (TARGET_BYTE_INT ? "-mint8" : "-mint16"));
|
||
+ if (TARGET_EXPERIMENT)
|
||
+ fprintf (asm_out_file, ";;; -mexperiment\n");
|
||
+ if (TARGET_WPC)
|
||
+ fprintf (asm_out_file, ";;; -mwpc\n");
|
||
+ if (TARGET_6309)
|
||
+ fprintf (asm_out_file, ";;; -m6309\n");
|
||
+
|
||
+ /* Print the name of the module, which is taken as the base name
|
||
+ * of the input file.
|
||
+ * See the 'User-Defined Symbols' section of the assembler
|
||
+ * documentation for the rules on valid symbols.
|
||
+ */
|
||
+ module_name = lbasename (main_input_filename);
|
||
+
|
||
+ fprintf (asm_out_file, "\t.module\t");
|
||
+
|
||
+ if (*module_name >= '0' && *module_name <= '9')
|
||
+ fprintf (asm_out_file, "_");
|
||
+
|
||
+ while (*module_name)
|
||
+ {
|
||
+ if ((*module_name >= '0' && *module_name <= '9')
|
||
+ || (*module_name >= 'A' && *module_name <= 'Z')
|
||
+ || (*module_name >= 'a' && *module_name <= 'z')
|
||
+ || *module_name == '$'
|
||
+ || *module_name == '.'
|
||
+ || *module_name == '_')
|
||
+ {
|
||
+ fprintf (asm_out_file, "%c", *module_name);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ fprintf (asm_out_file, "_");
|
||
+ }
|
||
+ module_name++;
|
||
+ }
|
||
+
|
||
+ fprintf (asm_out_file, "\n");
|
||
+}
|
||
+
|
||
+
|
||
+/** Returns true if prologue/epilogue code is required for the
|
||
+ * current function being compiled.
|
||
+ *
|
||
+ * This is just the inverse of whether the function is declared as
|
||
+ * 'naked'.
|
||
+ */
|
||
+int
|
||
+prologue_epilogue_required (void)
|
||
+{
|
||
+ return !m6809_current_function_has_type_attr_p ("naked")
|
||
+ && !m6809_current_function_has_type_attr_p ("noreturn");
|
||
+}
|
||
+
|
||
+
|
||
+/** Expand RTL for function entry */
|
||
+void
|
||
+emit_prologue_insns (void)
|
||
+{
|
||
+ rtx insn;
|
||
+ unsigned int live_regs = m6809_get_live_regs ();
|
||
+ unsigned int frame_size = get_frame_size ();
|
||
+
|
||
+ /* Save all registers used, including the frame pointer */
|
||
+ if (live_regs && !m6809_current_function_has_type_attr_p ("interrupt"))
|
||
+ {
|
||
+ insn = emit_insn (
|
||
+ gen_rtx_register_pushpop (UNSPEC_PUSH_RS, live_regs));
|
||
+ RTX_FRAME_RELATED_P (insn) = 1;
|
||
+ }
|
||
+
|
||
+ /* Allocate space for local variables */
|
||
+ if (frame_size != 0)
|
||
+ {
|
||
+ insn = emit_insn (gen_rtx_stack_adjust (MINUS, frame_size));
|
||
+ RTX_FRAME_RELATED_P (insn) = 1;
|
||
+ }
|
||
+
|
||
+ /* Set the frame pointer if it is needed */
|
||
+ if (frame_pointer_needed)
|
||
+ {
|
||
+ insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
||
+ RTX_FRAME_RELATED_P (insn) = 1;
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/** Expand RTL for function exit */
|
||
+void
|
||
+emit_epilogue_insns (bool sibcall_p)
|
||
+{
|
||
+ unsigned int live_regs = m6809_get_live_regs ();
|
||
+ unsigned int frame_size = get_frame_size ();
|
||
+
|
||
+ if (frame_size != 0)
|
||
+ emit_insn (gen_rtx_stack_adjust (PLUS, frame_size));
|
||
+
|
||
+ if (sibcall_p)
|
||
+ {
|
||
+ if (live_regs)
|
||
+ emit_insn (gen_rtx_register_pushpop (UNSPEC_POP_RS, live_regs));
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ if (live_regs && !m6809_current_function_has_type_attr_p ("interrupt"))
|
||
+ emit_insn (
|
||
+ gen_rtx_register_pushpop (UNSPEC_POP_RS, PC_REGBIT | live_regs));
|
||
+
|
||
+ if (m6809_current_function_has_type_attr_p ("interrupt"))
|
||
+ emit_jump_insn (gen_return_rti ());
|
||
+ else
|
||
+ emit_jump_insn (gen_return_rts ());
|
||
+ }
|
||
+}
|
||
+
|
||
+#if 0
|
||
+/** Predefine some preprocessor names according to the currently
|
||
+ * selected compiler options */
|
||
+void
|
||
+m6809_cpu_cpp_builtins (void)
|
||
+{
|
||
+ if (TARGET_6309)
|
||
+ {
|
||
+ builtin_define_std ("__M6309__");
|
||
+ builtin_define_std ("__m6309__");
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ builtin_define_std ("__M6809__");
|
||
+ builtin_define_std ("__m6809__");
|
||
+ }
|
||
+
|
||
+ if (TARGET_BYTE_INT)
|
||
+ builtin_define_std ("__int8__");
|
||
+ else
|
||
+ builtin_define_std ("__int16__");
|
||
+
|
||
+ switch (m6809_abi_version)
|
||
+ {
|
||
+ case M6809_ABI_VERSION_STACK:
|
||
+ builtin_define_std ("__regargs__");
|
||
+ builtin_define_std ("__ABI_STACK__");
|
||
+ break;
|
||
+ case M6809_ABI_VERSION_REGS:
|
||
+ builtin_define_std ("__ABI_REGS__");
|
||
+ break;
|
||
+ case M6809_ABI_VERSION_BX:
|
||
+ builtin_define_std ("__ABI_BX__");
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ if (TARGET_WPC)
|
||
+ builtin_define_std ("__WPC__");
|
||
+
|
||
+ if (TARGET_DRET)
|
||
+ builtin_define_std ("__DRET__");
|
||
+}
|
||
+#endif
|
||
+
|
||
+#define MAX_ASM_ASCII_STRING 48
|
||
+
|
||
+void
|
||
+m6809_output_ascii (FILE *fp, const char *str, unsigned long size)
|
||
+{
|
||
+ unsigned long i;
|
||
+ bool use_ascii = true;
|
||
+
|
||
+ /* If the size is too large, then break this up into multiple
|
||
+ outputs. The assembler can only output roughly 48 bytes at a
|
||
+ time. Note that if there are lots of escape sequences in
|
||
+ the string, this may fail. */
|
||
+ if (size > MAX_ASM_ASCII_STRING)
|
||
+ {
|
||
+ m6809_output_ascii (fp, str, MAX_ASM_ASCII_STRING);
|
||
+ m6809_output_ascii (fp, str + MAX_ASM_ASCII_STRING,
|
||
+ size - MAX_ASM_ASCII_STRING);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Check for 8-bit codes, which cannot be embedded in an .ascii */
|
||
+ for (i = 0; i < size; i++)
|
||
+ {
|
||
+ int c = str[i] & 0377;
|
||
+ if (c >= 0x80)
|
||
+ {
|
||
+ use_ascii = false;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (use_ascii)
|
||
+ fprintf (fp, "\t.ascii \"");
|
||
+
|
||
+ for (i = 0; i < size; i++)
|
||
+ {
|
||
+ int c = str[i] & 0377;
|
||
+
|
||
+ if (use_ascii)
|
||
+ {
|
||
+ /* Just output the plain character if it is printable,
|
||
+ otherwise output the escape code for the character.
|
||
+ The assembler recognizes the same C-style octal escape sequences,
|
||
+ except that it only supports 7-bit codes. */
|
||
+ if (c >= ' ' && c < 0177 && c != '\\' && c != '"')
|
||
+ putc (c, fp);
|
||
+ else switch (c)
|
||
+ {
|
||
+ case '\n':
|
||
+#ifndef TARGET_COCO
|
||
+ fputs ("\\n", fp);
|
||
+ break;
|
||
+#endif
|
||
+ /* On the CoCo, we fallthrough and treat '\n' like '\r'. */
|
||
+ case '\r':
|
||
+ fputs ("\\r", fp);
|
||
+ break;
|
||
+ case '\t':
|
||
+ fputs ("\\t", fp);
|
||
+ break;
|
||
+ case '\f':
|
||
+ fputs ("\\f", fp);
|
||
+ break;
|
||
+ case 0:
|
||
+ fputs ("\\0", fp);
|
||
+ break;
|
||
+ default:
|
||
+ fprintf (fp, "\\%03o", c);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ fprintf (fp, "\t.byte\t0x%02X\n", c);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (use_ascii)
|
||
+ fprintf (fp, "\"\n");
|
||
+}
|
||
+
|
||
+
|
||
+void
|
||
+m6809_output_quoted_string (FILE *asm_file, const char *string)
|
||
+{
|
||
+ char c;
|
||
+
|
||
+ if (strlen (string) > MAX_ASM_ASCII_STRING)
|
||
+ {
|
||
+ /* The string length is too large. We'll have to truncate it.
|
||
+ This is only called from debugging functions, so it's usually
|
||
+ not critical. */
|
||
+
|
||
+ char truncated_string[MAX_ASM_ASCII_STRING+1];
|
||
+
|
||
+ /* Copy as many characters as we can. */
|
||
+ strncpy (truncated_string, string, MAX_ASM_ASCII_STRING);
|
||
+ truncated_string[MAX_ASM_ASCII_STRING] = '\0';
|
||
+ string = truncated_string;
|
||
+ }
|
||
+
|
||
+ /* Copied from toplev.c */
|
||
+
|
||
+ putc ('\"', asm_file);
|
||
+ while ((c = *string++) != 0) {
|
||
+ if (ISPRINT (c)) {
|
||
+ if (c == '\"' || c == '\\')
|
||
+ putc ('\\', asm_file);
|
||
+ putc (c, asm_file);
|
||
+ }
|
||
+ else
|
||
+ fprintf (asm_file, "\\%03o", (unsigned char) c);
|
||
+ }
|
||
+ putc ('\"', asm_file);
|
||
+}
|
||
+
|
||
+
|
||
+/** Output the assembly code for a shift instruction where the
|
||
+ * shift count is not constant. */
|
||
+void
|
||
+m6809_output_shift_insn (int rtx_code, rtx *operands)
|
||
+{
|
||
+ struct shift_opcode *op;
|
||
+
|
||
+ if (GET_CODE (operands[2]) == CONST_INT)
|
||
+ abort ();
|
||
+
|
||
+ if (optimize_size && GET_MODE (operands[0]) == HImode)
|
||
+ {
|
||
+ switch (rtx_code)
|
||
+ {
|
||
+ case ASHIFT:
|
||
+ output_asm_insn ("jsr\t_ashlhi3", operands);
|
||
+ break;
|
||
+ case ASHIFTRT:
|
||
+ output_asm_insn ("jsr\t_ashrhi3", operands);
|
||
+ break;
|
||
+ case LSHIFTRT:
|
||
+ output_asm_insn ("jsr\t_lshrhi3", operands);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ else if (GET_MODE (operands[0]) == HImode)
|
||
+ {
|
||
+ switch (rtx_code)
|
||
+ {
|
||
+ case ASHIFT:
|
||
+ m6809_gen_register_shift (operands, "aslb", "rola");
|
||
+ break;
|
||
+ case ASHIFTRT:
|
||
+ m6809_gen_register_shift (operands, "asra", "rorb");
|
||
+ break;
|
||
+ case LSHIFTRT:
|
||
+ m6809_gen_register_shift (operands, "lsra", "rorb");
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ switch (rtx_code)
|
||
+ {
|
||
+ case ASHIFT:
|
||
+ m6809_gen_register_shift (operands, "aslb", NULL);
|
||
+ break;
|
||
+ case ASHIFTRT:
|
||
+ m6809_gen_register_shift (operands, "asrb", NULL);
|
||
+ break;
|
||
+ case LSHIFTRT:
|
||
+ m6809_gen_register_shift (operands, "lsrb", NULL);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+void
|
||
+m6809_emit_move_insn (rtx dst, rtx src)
|
||
+{
|
||
+ emit_insn (gen_rtx_SET (VOIDmode, dst, src));
|
||
+ if (ACC_A_REG_P (dst))
|
||
+ emit_insn (gen_rtx_USE (VOIDmode, dst));
|
||
+}
|
||
+
|
||
+
|
||
+/** Split a complex shift instruction into multiple CPU
|
||
+ * shift instructions. */
|
||
+void
|
||
+m6809_split_shift (enum rtx_code code, rtx *operands)
|
||
+{
|
||
+ enum machine_mode mode;
|
||
+ int count;
|
||
+
|
||
+ mode = GET_MODE (operands[0]);
|
||
+ count = INTVAL (operands[2]);
|
||
+
|
||
+ /* Handle a shift count outside the range of 0 .. N-1, where
|
||
+ * N is the mode size in bits. We normalize the count, and
|
||
+ * for negative counts we also invert the direction of the
|
||
+ * shift. */
|
||
+ if ((count < 0) || (count >= 8 * GET_MODE_SIZE (mode)))
|
||
+ {
|
||
+ if (count < 0)
|
||
+ {
|
||
+ count = -count;
|
||
+ code = (code == ASHIFT) ? ASHIFTRT : ASHIFT;
|
||
+ }
|
||
+ count %= (8 * GET_MODE_SIZE (mode));
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (code, mode, operands[1],
|
||
+ gen_rtx_CONST_INT (VOIDmode, count)));
|
||
+ }
|
||
+
|
||
+ /* Handle shift by zero explicitly as a no-op. */
|
||
+ if (count == 0)
|
||
+ {
|
||
+ emit_insn (gen_nop ());
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Decompose the shift by a constant N > 8 into two
|
||
+ * shifts, first by 8 and then by N-8.
|
||
+ * This "speeds up" the process for large shifts that would be
|
||
+ * handled below, but allows for some optimization.
|
||
+ * In some cases shift by 8 can be implemented fast. If an
|
||
+ * instruction to shift by 8 is defined, it will be used here;
|
||
+ * otherwise it will be further decomposed as below. */
|
||
+ if (mode == HImode && count > 8)
|
||
+ {
|
||
+ rtx output = operands[0];
|
||
+
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (code, mode, operands[1],
|
||
+ gen_rtx_CONST_INT (VOIDmode, 8)));
|
||
+
|
||
+ /* Unsigned shifts always produce a zero in either the
|
||
+ * upper or lower half of the output; then, that part
|
||
+ * does not need to be shifted anymore. We modify the
|
||
+ * output and the subsequent instructions to operate in
|
||
+ * QImode only on the relevant part. */
|
||
+ if (REG_P (output))
|
||
+ {
|
||
+ if (code == ASHIFT)
|
||
+ {
|
||
+ output = gen_rtx_REG (QImode, HARD_A_REGNUM);
|
||
+ mode = QImode;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output = gen_rtx_REG (QImode, HARD_D_REGNUM);
|
||
+ mode = QImode;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ m6809_emit_move_insn (output,
|
||
+ gen_rtx_fmt_ee (code, mode, copy_rtx (output),
|
||
+ gen_rtx_CONST_INT (VOIDmode, count-8)));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Rewrite the unsigned shift of an 8-bit register by a large constant N
|
||
+ * (near to the maximum of 8) as a rotate and mask. */
|
||
+ if (mode == QImode && REG_P (operands[0]) && count >= ((code == ASHIFTRT) ? 7 : 6))
|
||
+ {
|
||
+ unsigned int mask;
|
||
+ unsigned int was_signed = (code == ASHIFTRT);
|
||
+
|
||
+ code = (code == ASHIFT) ? ROTATERT : ROTATE;
|
||
+ if (code == ROTATE)
|
||
+ mask = (count == 6) ? 0x03 : 0x01;
|
||
+ else
|
||
+ mask = (count == 6) ? 0xC0 - 0x100 : 0x80 - 0x100;
|
||
+ count = 9 - count;
|
||
+
|
||
+ do {
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (code, QImode, operands[1], const1_rtx));
|
||
+ } while (--count != 0);
|
||
+
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (AND, QImode, operands[1],
|
||
+ gen_rtx_CONST_INT (VOIDmode, mask)));
|
||
+
|
||
+ if (was_signed)
|
||
+ {
|
||
+ emit_insn (gen_negqi2 (operands[0], copy_rtx (operands[0])));
|
||
+ if (ACC_A_REG_P (operands[0]))
|
||
+ emit_insn (gen_rtx_USE (VOIDmode, operands[0]));
|
||
+ }
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Decompose the shift by any constant N > 1 into a sequence
|
||
+ * of N shifts.
|
||
+ * This is done recursively, by creating a shift by 1 and a
|
||
+ * shift by N-1, as long as N>1. */
|
||
+ if (count > 1)
|
||
+ {
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (code, mode, operands[1], const1_rtx));
|
||
+
|
||
+ m6809_emit_move_insn (operands[0],
|
||
+ gen_rtx_fmt_ee (code, mode, operands[1],
|
||
+ gen_rtx_CONST_INT (VOIDmode, count-1)));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Decompose the single shift of a 16-bit quantity into two
|
||
+ * CPU instructions, one for each 8-bit half.
|
||
+ */
|
||
+ if (mode == HImode && count == 1)
|
||
+ {
|
||
+ rtx first, second;
|
||
+ enum rtx_code rotate_code;
|
||
+
|
||
+ rotate_code = (code == ASHIFT) ? ROTATE : ROTATERT;
|
||
+
|
||
+ /* Split the operand into two 8-bit entities.
|
||
+ * FIRST is the one that will get shifted via a regular CPU
|
||
+ * instruction.
|
||
+ * SECOND is the one that will have the result of the first shift
|
||
+ * rotated in.
|
||
+ *
|
||
+ * We initialize first and second as if we are doing a left shift,
|
||
+ * then swap the operands if it's a right shift.
|
||
+ */
|
||
+ if (REG_P (operands[0]))
|
||
+ {
|
||
+ first = gen_rtx_REG (QImode, HARD_D_REGNUM); /* HARD_B_REGNUM? */
|
||
+ second = gen_rtx_REG (QImode, HARD_A_REGNUM);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ first = adjust_address (operands[0], QImode, 1);
|
||
+ second = adjust_address (operands[0], QImode, 0);
|
||
+ }
|
||
+
|
||
+ if (rotate_code == ROTATERT)
|
||
+ {
|
||
+ rtx tmp; tmp = first; first = second; second = tmp;
|
||
+ }
|
||
+
|
||
+ /* Decompose into a shift and a rotate instruction. */
|
||
+ m6809_emit_move_insn (first,
|
||
+ gen_rtx_fmt_ee (code, QImode, copy_rtx (first), const1_rtx));
|
||
+ m6809_emit_move_insn (second,
|
||
+ gen_rtx_fmt_ee (rotate_code, QImode, copy_rtx (second), const1_rtx));
|
||
+ return;
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/** Adjust register usage based on compile-time flags. */
|
||
+void
|
||
+m6809_conditional_register_usage (void)
|
||
+{
|
||
+ unsigned int soft_regno;
|
||
+
|
||
+#ifdef CONFIG_SOFT_REGS_ALWAYS
|
||
+ m6809_soft_regs = CONFIG_SOFT_REGS_ALWAYS;
|
||
+#else
|
||
+ if (!m6809_soft_reg_count)
|
||
+ return;
|
||
+ m6809_soft_regs = atoi (m6809_soft_reg_count);
|
||
+#endif
|
||
+
|
||
+ if (m6809_soft_regs == 0)
|
||
+ return;
|
||
+
|
||
+ if (m6809_soft_regs > NUM_M_REGS)
|
||
+ m6809_soft_regs = NUM_M_REGS;
|
||
+
|
||
+ /* Registers are marked FIXED by default. Free up if
|
||
+ the user wishes. */
|
||
+ for (soft_regno = 1; soft_regno < m6809_soft_regs; soft_regno++)
|
||
+ {
|
||
+ fixed_regs[SOFT_M0_REGNUM + soft_regno] = 0;
|
||
+
|
||
+ /* Mark the softregs as call-clobbered, so that they need
|
||
+ * not be saved/restored on function entry/exit. */
|
||
+ call_used_regs[SOFT_M0_REGNUM + soft_regno] = 1;
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/** Return a RTX representing how to return a value from a function.
|
||
+ VALTYPE gives the type of the value, FUNC identifies the function
|
||
+ itself.
|
||
+
|
||
+ In general, we only care about the width of the result. */
|
||
+rtx
|
||
+m6809_function_value (const tree valtype, const tree func ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ unsigned int regno;
|
||
+ enum machine_mode mode;
|
||
+
|
||
+ /* Get the mode (i.e. width) of the result. */
|
||
+ mode = TYPE_MODE (valtype);
|
||
+
|
||
+ if (lookup_attribute ("boolean", TYPE_ATTRIBUTES (valtype)))
|
||
+ regno = HARD_Z_REGNUM;
|
||
+ else if (mode == QImode || (TARGET_DRET && mode == HImode))
|
||
+ regno = HARD_D_REGNUM;
|
||
+ else
|
||
+ regno = HARD_X_REGNUM;
|
||
+ return gen_rtx_REG (mode, regno);
|
||
+}
|
||
+
|
||
+
|
||
+/** Return 1 if REGNO is possibly needed to return the result
|
||
+of a function, 0 otherwise. */
|
||
+int
|
||
+m6809_function_value_regno_p (unsigned int regno)
|
||
+{
|
||
+ if (regno == HARD_Z_REGNUM)
|
||
+ return 1;
|
||
+ else if ((TARGET_BYTE_INT || TARGET_DRET) && regno == HARD_D_REGNUM)
|
||
+ return 1;
|
||
+ else if (!TARGET_DRET && regno == HARD_X_REGNUM)
|
||
+ return 1;
|
||
+ else
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef TRACE_PEEPHOLE
|
||
+int
|
||
+m6809_match_peephole2 (unsigned int peephole_id, unsigned int stage)
|
||
+{
|
||
+ if (stage == PEEP_END)
|
||
+ {
|
||
+ printf ("%s: peephole %d pattern and predicate matched\n",
|
||
+ main_input_filename, peephole_id);
|
||
+ fflush (stdout);
|
||
+ }
|
||
+ else if (stage == PEEP_COND)
|
||
+ {
|
||
+ printf ("%s: peephole %d? at least pattern matched\n",
|
||
+ main_input_filename, peephole_id);
|
||
+ fflush (stdout);
|
||
+ }
|
||
+ return 1;
|
||
+}
|
||
+#else
|
||
+int
|
||
+m6809_match_peephole2 (unsigned int peephole_id ATTRIBUTE_UNUSED,
|
||
+ unsigned int stage ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ return 1;
|
||
+}
|
||
+#endif /* TRACE_PEEPHOLE */
|
||
+
|
||
+
|
||
+/** Return 1 if it is OK to store a value of MODE in REGNO. */
|
||
+int
|
||
+m6809_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
|
||
+{
|
||
+ /* Soft registers, as they are just memory, can really hold
|
||
+ values of any type. However we restrict them to values of
|
||
+ size HImode or QImode to prevent exhausting them for larger
|
||
+ values.
|
||
+ Word values cannot be placed into the first soft register,
|
||
+ as it is the low byte that is being placed there, which
|
||
+ corrupts the (non-soft) register before it. */
|
||
+ if (M_REGNO_P (regno))
|
||
+ {
|
||
+ switch (GET_MODE_SIZE (mode))
|
||
+ {
|
||
+ case 1:
|
||
+ return 1;
|
||
+ case 2:
|
||
+ return regno != SOFT_M0_REGNUM;
|
||
+ default:
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* VOIDmode can be stored anywhere */
|
||
+ else if (mode == VOIDmode)
|
||
+ return 1;
|
||
+
|
||
+ /* Zero is a reserved register, but problems occur if we don't
|
||
+ say yes here??? */
|
||
+ else if (regno == 0)
|
||
+ return 1;
|
||
+
|
||
+ /* For other registers, return true only if the requested size
|
||
+ exactly matches the hardware size. */
|
||
+ else if ((G_REGNO_P (regno)) && (GET_MODE_SIZE (mode) == 2))
|
||
+ return 1;
|
||
+ else if ((BYTE_REGNO_P (regno)) && (GET_MODE_SIZE (mode) == 1))
|
||
+ return 1;
|
||
+ else
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+/* exp is the call expression. DECL is the called function,
|
||
+ * or NULL for an indirect call */
|
||
+bool
|
||
+m6809_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
||
+{
|
||
+ tree type, arg;
|
||
+ const char *name;
|
||
+ bool result = 0;
|
||
+ int argcount = 0;
|
||
+ int step = 1;
|
||
+
|
||
+ /* If there is no DECL, it is an indirect call.
|
||
+ * Never optimize this??? */
|
||
+ if (decl == NULL)
|
||
+ goto done;
|
||
+
|
||
+ /* Never allow an interrupt handler to be optimized this way. */
|
||
+ if (m6809_function_has_type_attr_p (decl, "interrupt"))
|
||
+ goto done;
|
||
+
|
||
+ /* Skip sibcall if the type can't be found for
|
||
+ * some reason */
|
||
+ step++;
|
||
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
|
||
+ type = TREE_TYPE (decl);
|
||
+ if (type == NULL)
|
||
+ goto done;
|
||
+
|
||
+ /* Skip sibcall if the target is a far function */
|
||
+ step++;
|
||
+ if (far_function_type_p (type) != NULL)
|
||
+ goto done;
|
||
+
|
||
+ /* Skip sibcall if the called function's arguments are
|
||
+ * variable */
|
||
+ step++;
|
||
+ if (TYPE_ARG_TYPES (type) == NULL)
|
||
+ goto done;
|
||
+
|
||
+ /* Allow sibcalls in other cases. */
|
||
+ result = 1;
|
||
+done:
|
||
+ /* printf ("%s ok for sibcall? %s, step %d, args %d\n", name, result ? "yes" : "no", step, argcount); */
|
||
+ return result;
|
||
+}
|
||
+
|
||
+
|
||
+/** Emit code for the 'casesi' pattern.
|
||
+ * This pattern is only used in 8-bit mode, and can be disabled
|
||
+ * with -mold-case there as well. The rationale for this is to
|
||
+ * do a better job than the simpler but well-tested 'tablejump'
|
||
+ * method.
|
||
+ *
|
||
+ * For small jumptables, where the switch expression is an
|
||
+ * 8-bit value, the lookup can be done more efficiently
|
||
+ * using the "B,X" style index mode. */
|
||
+void
|
||
+m6809_do_casesi (rtx index, rtx lower_bound, rtx range,
|
||
+ rtx table_label, rtx default_label)
|
||
+{
|
||
+ enum machine_mode mode;
|
||
+ rtx scaled;
|
||
+ rtx table_in_reg;
|
||
+
|
||
+ /* expr.c has to be patched so that it does not promote
|
||
+ * the expression to SImode, but rather to HImode.
|
||
+ * Fail now if that isn't the case. */
|
||
+ if (GET_MODE_SIZE (GET_MODE (index)) > GET_MODE_SIZE (HImode))
|
||
+ error ("try_casesi promotion bug");
|
||
+
|
||
+ /* Determine whether or not we are going to work primarily in
|
||
+ * QImode or HImode. This depends on the size of the index
|
||
+ * into the lookup table. QImode can only be used when the
|
||
+ * index is less than 0x40, since it will be doubled but
|
||
+ * must remain unsigned. */
|
||
+ if ((GET_CODE (range) == CONST_INT) && (INTVAL (range) < 0x40))
|
||
+ mode = QImode;
|
||
+ else
|
||
+ mode = HImode;
|
||
+
|
||
+ /* Convert to QImode if necessary */
|
||
+ if (mode == QImode)
|
||
+ {
|
||
+ index = gen_lowpart_general (mode, index);
|
||
+ lower_bound = gen_lowpart_general (mode, lower_bound);
|
||
+ }
|
||
+
|
||
+ /* Translate from case value to table index by subtraction */
|
||
+ if (lower_bound != const0_rtx)
|
||
+ index = expand_binop (mode, sub_optab, index, lower_bound,
|
||
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
|
||
+
|
||
+ /* Emit compare-and-jump to test for index out-of-range */
|
||
+ emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, mode, 1,
|
||
+ default_label);
|
||
+
|
||
+ /* Put the table address is in a register */
|
||
+ table_in_reg = gen_reg_rtx (Pmode);
|
||
+ emit_move_insn (table_in_reg, gen_rtx_LABEL_REF (Pmode, table_label));
|
||
+
|
||
+ /* Emit table lookup and jump */
|
||
+ if (mode == QImode)
|
||
+ {
|
||
+ /* Scale the index */
|
||
+ scaled = gen_reg_rtx (QImode);
|
||
+ emit_insn (gen_ashlqi3 (scaled, index, const1_rtx));
|
||
+
|
||
+ /* Emit the jump */
|
||
+ emit_jump_insn (gen_tablejump_short_offset (scaled, table_in_reg));
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ /* Scale the index */
|
||
+ emit_insn (gen_ashlhi3 (index, index, const1_rtx));
|
||
+
|
||
+ /* Emit the jump */
|
||
+ emit_jump_insn (gen_tablejump_long_offset (index, table_in_reg));
|
||
+ }
|
||
+
|
||
+ /* Copied from expr.c */
|
||
+ if (!CASE_VECTOR_PC_RELATIVE && !flag_pic)
|
||
+ emit_barrier ();
|
||
+}
|
||
+
|
||
+
|
||
+/** Output the assembly code for a 32-bit add/subtract. */
|
||
+void
|
||
+m6809_output_addsi3 (int rtx_code, rtx *operands)
|
||
+{
|
||
+ rtx xoperands[8];
|
||
+ rtx dst = operands[0];
|
||
+
|
||
+ /* Prepare the operands by splitting each SImode into two HImodes
|
||
+ that can be operated independently. The high word of operand 1
|
||
+ is further divided into two QImode components for use with 'adc'
|
||
+ style instructions. */
|
||
+ xoperands[7] = operands[3];
|
||
+
|
||
+ xoperands[0] = adjust_address (dst, HImode, 2);
|
||
+ xoperands[3] = adjust_address (dst, HImode, 0);
|
||
+
|
||
+#if 1
|
||
+ xoperands[2] = adjust_address (operands[1], HImode, 2);
|
||
+ xoperands[6] = adjust_address (operands[1], HImode, 0);
|
||
+
|
||
+ /* Operand 2 may be a MEM or a CONST_INT */
|
||
+ if (GET_CODE (operands[2]) == CONST_INT)
|
||
+ {
|
||
+ xoperands[1] = gen_int_mode (INTVAL (operands[2]) & 0xFFFF, HImode);
|
||
+ xoperands[4] = gen_int_mode ((INTVAL (operands[2]) >> 24) & 0xFF, QImode);
|
||
+ xoperands[5] = gen_int_mode ((INTVAL (operands[2]) >> 16) & 0xFF, QImode);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ xoperands[1] = adjust_address (operands[2], HImode, 2);
|
||
+ xoperands[4] = adjust_address (operands[2], QImode, 0);
|
||
+ xoperands[5] = adjust_address (operands[2], QImode, 1);
|
||
+ }
|
||
+
|
||
+#endif
|
||
+
|
||
+#if 0
|
||
+ xoperands[1] = adjust_address (operands[1], HImode, 2);
|
||
+ xoperands[4] = adjust_address (operands[1], QImode, 0);
|
||
+ xoperands[5] = adjust_address (operands[1], QImode, 1);
|
||
+
|
||
+ /* Operand 2 may be a MEM or a CONST_INT */
|
||
+ if (GET_CODE (operands[2]) == CONST_INT)
|
||
+ {
|
||
+ xoperands[2] = gen_int_mode ((INTVAL (operands[2])) & 0xFFFF, HImode);
|
||
+ xoperands[6] = gen_int_mode ((INTVAL (operands[2]) >> 16) & 0xFFFF, HImode);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ xoperands[2] = adjust_address (operands[2], HImode, 2);
|
||
+ xoperands[6] = adjust_address (operands[2], HImode, 0);
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ /* Output the assembly code. */
|
||
+ if (rtx_code == PLUS)
|
||
+ {
|
||
+ output_asm_insn ("ld%7\t%2", xoperands);
|
||
+ output_asm_insn ("add%7\t%1", xoperands);
|
||
+ output_asm_insn ("st%7\t%0", xoperands);
|
||
+ output_asm_insn ("ld%7\t%6", xoperands);
|
||
+ output_asm_insn ("adcb\t%5", xoperands);
|
||
+ output_asm_insn ("adca\t%4", xoperands);
|
||
+ output_asm_insn ("st%7\t%3", xoperands);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ output_asm_insn ("ld%7\t%2", xoperands);
|
||
+ output_asm_insn ("sub%7\t%1", xoperands);
|
||
+ output_asm_insn ("st%7\t%0", xoperands);
|
||
+ output_asm_insn ("ld%7\t%6", xoperands);
|
||
+ output_asm_insn ("sbcb\t%5", xoperands);
|
||
+ output_asm_insn ("sbca\t%4", xoperands);
|
||
+ output_asm_insn ("st%7\t%3", xoperands);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#if 0
|
||
+/** Output the assembly code for a 32-bit shift.
|
||
+Operands 0 and 1 must be the same rtx, forced by a matching
|
||
+constraint. Operand 2 must be a CONST_INT. Operand 3 is
|
||
+"d" in case a temporary reg is needed. */
|
||
+void
|
||
+m6809_output_shiftsi3 (int rtx_code, rtx *operands)
|
||
+{
|
||
+ unsigned int count = INTVAL (operands[2]) % 32;
|
||
+ unsigned int size = 4; /* sizeof (SImode) */
|
||
+ int s;
|
||
+ rtx xoperands[4];
|
||
+ int op;
|
||
+ int start, end, step;
|
||
+
|
||
+ /* Initialize */
|
||
+ if (rtx_code == ASHIFT)
|
||
+ {
|
||
+ start = size-1;
|
||
+ end = -1;
|
||
+ step = -1;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ start = 0;
|
||
+ end = size;
|
||
+ step = 1;
|
||
+ }
|
||
+
|
||
+ xoperands[2] = operands[2];
|
||
+ xoperands[3] = operands[3];
|
||
+
|
||
+ if (count <= 0)
|
||
+ abort ();
|
||
+ if (rtx_code == ROTATE || rtx_code == ROTATERT)
|
||
+ abort ();
|
||
+
|
||
+ /* Extract bit shifts over 16 bits by HImode moves. */
|
||
+ if (count >= 16)
|
||
+ {
|
||
+ }
|
||
+
|
||
+ /* Extract bit shifts over 8 bits by QImode moves. */
|
||
+ if (count >= 8)
|
||
+ {
|
||
+ }
|
||
+
|
||
+ /* Iterate over the number of bits to be shifted. */
|
||
+ while (count > 0)
|
||
+ {
|
||
+ /* Each bit to be shifted requires 1 proper bit shift
|
||
+ and 3 rotates. */
|
||
+
|
||
+ /* First, do the arithmetic/logical shift. Left shifts
|
||
+ start from the LSB; right shifts start from the MSB. */
|
||
+ xoperands[0] = adjust_address (operands[0], QImode, start);
|
||
+ switch (rtx_code)
|
||
+ {
|
||
+ case ASHIFT:
|
||
+ output_asm_insn ("asl\t%0", xoperands);
|
||
+ start--;
|
||
+ break;
|
||
+ case ASHIFTRT:
|
||
+ output_asm_insn ("asr\t%0", xoperands);
|
||
+ start++;
|
||
+ break;
|
||
+ case LSHIFTRT:
|
||
+ output_asm_insn ("lsr\t%0", xoperands);
|
||
+ start++;
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ /* Next, rotate the other bytes */
|
||
+ for (s = start; s != end; s += step)
|
||
+ {
|
||
+ xoperands[0] = adjust_address (operands[0], QImode, s);
|
||
+ switch (rtx_code)
|
||
+ {
|
||
+ case ASHIFT:
|
||
+ output_asm_insn ("rol\t%0", xoperands);
|
||
+ break;
|
||
+ case ASHIFTRT:
|
||
+ case LSHIFTRT:
|
||
+ output_asm_insn ("ror\t%0", xoperands);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ count--;
|
||
+ }
|
||
+}
|
||
+#endif
|
||
+
|
||
+int
|
||
+power_of_two_p (unsigned int n)
|
||
+{
|
||
+ return (n & (n-1)) == 0;
|
||
+}
|
||
+
|
||
+
|
||
+int
|
||
+m6809_can_eliminate (int from, int to)
|
||
+{
|
||
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
|
||
+ return !frame_pointer_needed;
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+
|
||
+int
|
||
+m6809_initial_elimination_offset (int from, int to)
|
||
+{
|
||
+ switch (from)
|
||
+ {
|
||
+ case ARG_POINTER_REGNUM:
|
||
+ return get_frame_size () + m6809_get_regs_size (m6809_get_live_regs ());
|
||
+ case FRAME_POINTER_REGNUM:
|
||
+ return get_frame_size ();
|
||
+ default:
|
||
+ gcc_unreachable ();
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+bool
|
||
+m6809_frame_pointer_required (void)
|
||
+{
|
||
+ return false;
|
||
+}
|
||
+
|
||
+
|
||
+/* Defines the target-specific hooks structure. */
|
||
+struct gcc_target targetm = TARGET_INITIALIZER;
|
||
diff --git a/gcc/config/m6809/m6809.h b/gcc/config/m6809/m6809.h
|
||
new file mode 100644
|
||
index 0000000..386bcc9
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/m6809.h
|
||
@@ -0,0 +1,1352 @@
|
||
+/* Definitions of target machine for GNU compiler. MC6809 version.
|
||
+
|
||
+ MC6809 Version by Tom Jones (jones@sal.wisc.edu)
|
||
+ Space Astronomy Laboratory
|
||
+ University of Wisconsin at Madison
|
||
+
|
||
+ minor changes to adapt it to gcc-2.5.8 by Matthias Doerfel
|
||
+ ( msdoerfe@informatik.uni-erlangen.de )
|
||
+ also added #pragma interrupt (inspired by gcc-6811)
|
||
+
|
||
+ minor changes to adapt it to gcc-2.8.0 by Eric Botcazou
|
||
+ (ebotcazou@multimania.com)
|
||
+
|
||
+ minor changes to adapt it to egcs-1.1.2 by Eric Botcazou
|
||
+ (ebotcazou@multimania.com)
|
||
+
|
||
+ minor changes to adapt it to gcc-2.95.3 by Eric Botcazou
|
||
+ (ebotcazou@multimania.com)
|
||
+
|
||
+ changes for gcc-3.1.1 by ???
|
||
+
|
||
+ further changes for gcc-3.1.1 and beyond by Brian Dominy
|
||
+ (brian@oddchange.com)
|
||
+
|
||
+ even more changes for gcc-4.6.1 by William Astle (lost@l-w.ca)
|
||
+
|
||
+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/>. */
|
||
+
|
||
+
|
||
+/* Helper macros for creating strings with macros */
|
||
+#define C_STRING(x) C_STR(x)
|
||
+#define C_STR(x) #x
|
||
+
|
||
+/* Certain parts of GCC include host-side includes, which is bad.
|
||
+ * Some things that get pulled in need to be undone.
|
||
+ */
|
||
+#undef HAVE_GAS_HIDDEN
|
||
+
|
||
+/* Names to predefine in the preprocessor for this target machine. */
|
||
+/*#define TARGET_CPU_CPP_BUILTINS() m6809_cpu_cpp_builtins () */
|
||
+#define TARGET_CPU_CPP_BUILTINS() do \
|
||
+ { \
|
||
+ if (TARGET_6309) \
|
||
+ { \
|
||
+ builtin_define_std ("__M6309__"); \
|
||
+ builtin_define_std ("__m6309__"); \
|
||
+ } \
|
||
+ else \
|
||
+ { \
|
||
+ builtin_define_std ("__M6809__"); \
|
||
+ builtin_define_std ("__m6809__"); \
|
||
+ } \
|
||
+ \
|
||
+ if (TARGET_BYTE_INT) \
|
||
+ builtin_define_std ("__int8__"); \
|
||
+ else \
|
||
+ builtin_define_std ("__int16__"); \
|
||
+ \
|
||
+ switch (m6809_abi_version) \
|
||
+ { \
|
||
+ case M6809_ABI_VERSION_STACK: \
|
||
+ builtin_define_std ("__regargs__"); \
|
||
+ builtin_define_std ("__ABI_STACK__"); \
|
||
+ break; \
|
||
+ case M6809_ABI_VERSION_REGS: \
|
||
+ builtin_define_std ("__ABI_REGS__"); \
|
||
+ break; \
|
||
+ case M6809_ABI_VERSION_BX: \
|
||
+ builtin_define_std ("__ABI_BX__"); \
|
||
+ break; \
|
||
+ default: \
|
||
+ break; \
|
||
+ } \
|
||
+ \
|
||
+ if (TARGET_WPC) \
|
||
+ builtin_define_std ("__WPC__"); \
|
||
+ \
|
||
+ if (TARGET_DRET) \
|
||
+ builtin_define_std ("__DRET__"); \
|
||
+ } while (0)
|
||
+
|
||
+/* As an embedded target, we have no libc. */
|
||
+#ifndef inhibit_libc
|
||
+#define inhibit_libc
|
||
+#endif
|
||
+
|
||
+/* Print subsidiary information on the compiler version in use. */
|
||
+#define TARGET_VERSION fprintf (stderr, " (MC6809)");
|
||
+
|
||
+/* Run-time compilation parameters selecting different hardware subsets. */
|
||
+/*extern int target_flags; */
|
||
+extern short *reg_renumber; /* def in local_alloc.c */
|
||
+
|
||
+/* Runtime current values of section names */
|
||
+extern int section_changed;
|
||
+extern char code_section_op[], data_section_op[], bss_section_op[];
|
||
+
|
||
+#define WARNING_OPT 0,
|
||
+/*extern const char *m6809_abi_version_ptr; */
|
||
+extern unsigned int m6809_soft_regs;
|
||
+extern unsigned int m6809_abi_version;
|
||
+
|
||
+/* ABI versions */
|
||
+
|
||
+#define M6809_ABI_VERSION_STACK 0
|
||
+#define M6809_ABI_VERSION_REGS 1
|
||
+#define M6809_ABI_VERSION_BX 2
|
||
+#define M6809_ABI_VERSION_LATEST (M6809_ABI_VERSION_BX)
|
||
+
|
||
+/* Allow $ in identifiers */
|
||
+#define DOLLARS_IN_IDENTIFIERS 1
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Target machine storage layout
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+/* Define this if most significant bit is lowest numbered
|
||
+ in instructions that operate on numbered bit-fields. */
|
||
+#define BITS_BIG_ENDIAN 0
|
||
+
|
||
+/* Define to 1 if most significant byte of a word is the lowest numbered. */
|
||
+#define BYTES_BIG_ENDIAN 1
|
||
+
|
||
+/* Define to 1 if most significant word of a multiword value is the lowest numbered. */
|
||
+#define WORDS_BIG_ENDIAN 1
|
||
+
|
||
+/* Number of bits in an addressible storage unit */
|
||
+#define BITS_PER_UNIT 8
|
||
+
|
||
+/* Width in bits of a "word", or the contents of a machine register.
|
||
+ * Although the 6809 has a few byte registers, define this to 16-bits
|
||
+ * since this is the natural size of most registers. */
|
||
+#define BITS_PER_WORD 16
|
||
+
|
||
+/* Width of a word, in units (bytes). */
|
||
+#define UNITS_PER_WORD (BITS_PER_WORD/8)
|
||
+
|
||
+/* Width in bits of a pointer. See also the macro `Pmode' defined below. */
|
||
+#define POINTER_SIZE 16
|
||
+
|
||
+/* Allocation boundary (bits) for storing pointers in memory. */
|
||
+#define POINTER_BOUNDARY 8
|
||
+
|
||
+/* Allocation boundary (bits) for storing arguments in argument list. */
|
||
+/* PARM_BOUNDARY is divided by BITS_PER_WORD in expr.c -- tej */
|
||
+#define PARM_BOUNDARY 8
|
||
+
|
||
+/* 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
|
||
+
|
||
+/* 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
|
||
+
|
||
+/* Largest mode size to use when putting an object, including
|
||
+ * a structure, into a register. By limiting this to 16, no
|
||
+ * 32-bit objects will ever be allocated to a pair of hard
|
||
+ * registers. This is a good thing, since there aren't that
|
||
+ * many of them. 32-bit objects are only needed for floats
|
||
+ * and "long long"s. Larger values have been tried and did not
|
||
+ * work. */
|
||
+#define MAX_FIXED_MODE_SIZE 16
|
||
+
|
||
+/* No data type wants to be aligned rounder than this. */
|
||
+#define BIGGEST_ALIGNMENT 8
|
||
+
|
||
+/* Define this if move instructions will actually fail to work
|
||
+ when given unaligned data. */
|
||
+#define STRICT_ALIGNMENT 0
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Standard register usage.
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+/* Register values as bitmasks.
|
||
+ * TODO : merge D_REGBIT and B_REGBIT, and treat this as the same
|
||
+ * register. */
|
||
+#define RSVD1_REGBIT (1 << HARD_RSVD1_REGNUM)
|
||
+#define D_REGBIT (1 << HARD_D_REGNUM)
|
||
+#define X_REGBIT (1 << HARD_X_REGNUM)
|
||
+#define Y_REGBIT (1 << HARD_Y_REGNUM)
|
||
+#define U_REGBIT (1 << HARD_U_REGNUM)
|
||
+#define S_REGBIT (1 << HARD_S_REGNUM)
|
||
+#define PC_REGBIT (1 << HARD_PC_REGNUM)
|
||
+#define Z_REGBIT (1 << HARD_Z_REGNUM)
|
||
+#define A_REGBIT (1 << HARD_A_REGNUM)
|
||
+#define B_REGBIT (1 << HARD_B_REGNUM)
|
||
+#define CC_REGBIT (1 << HARD_CC_REGNUM)
|
||
+#define DP_REGBIT (1 << HARD_DP_REGNUM)
|
||
+#define SOFT_FP_REGBIT (1 << SOFT_FP_REGNUM)
|
||
+#define SOFT_AP_REGBIT (1 << SOFT_AP_REGNUM)
|
||
+#define M_REGBIT(n) (1 << (SOFT_M0_REGNUM + n))
|
||
+
|
||
+/* Macros for dealing with set of registers.
|
||
+ * A register set is just a bitwise-OR of all the register
|
||
+ * bitmask values. */
|
||
+
|
||
+/* Which registers can hold 8-bits */
|
||
+#define BYTE_REGSET \
|
||
+ (Z_REGBIT | A_REGBIT | D_REGBIT | CC_REGBIT | DP_REGBIT)
|
||
+
|
||
+/* Which registers can hold 16-bits.
|
||
+ * Note: D_REGBIT is defined as both an 8-bit and 16-bit register */
|
||
+#define WORD_REGSET \
|
||
+ (D_REGBIT | X_REGBIT | Y_REGBIT | U_REGBIT | S_REGBIT | PC_REGBIT | SOFT_FP_REGBIT | SOFT_AP_REGBIT | RSVD1_REGBIT)
|
||
+
|
||
+/* Returns nonzero if a given REGNO is in the REGSET. */
|
||
+#define REGSET_CONTAINS_P(regno, regset) (((1 << (regno)) & (regset)) != 0)
|
||
+
|
||
+/* Defines related to the number of soft registers supported.
|
||
+ * The actual number used may be less depending on -msoft-reg-count.
|
||
+ * If you change one of these, you should change them all. */
|
||
+#define NUM_M_REGS 8
|
||
+#define M_REGS_FIXED 1, 1, 1, 1, 1, 1, 1, 1
|
||
+#define M_REGS_CALL_USED 1, 1, 1, 1, 1, 1, 1, 1
|
||
+#define HARD_M_REGNUMS \
|
||
+ SOFT_M0_REGNUM+0, SOFT_M0_REGNUM+1, SOFT_M0_REGNUM+2, SOFT_M0_REGNUM+3, \
|
||
+ SOFT_M0_REGNUM+4, SOFT_M0_REGNUM+5, SOFT_M0_REGNUM+6, SOFT_M0_REGNUM+7
|
||
+
|
||
+#define SOFT_M_REGBITS (((1UL << NUM_M_REGS) - 1) << (SOFT_M0_REGNUM))
|
||
+
|
||
+/* 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.
|
||
+ Make sure the constant below matches the value of SOFT_M0_REGNUM;
|
||
+ for some reason, GCC won't compile if that name is used here directly. */
|
||
+#ifdef SOFT_M0_REGNUM
|
||
+#if (SOFT_M0_REGNUM != 14)
|
||
+#error "bad register numbering"
|
||
+#endif
|
||
+#endif
|
||
+#define FIRST_PSEUDO_REGISTER (14 + NUM_M_REGS)
|
||
+
|
||
+/* 1 for registers that have pervasive standard uses
|
||
+ and are not available for the register allocator.
|
||
+ The psuedoregisters (M_REGS) are declared fixed here, but
|
||
+ will be unfixed if -msoft-reg-count is seen later. */
|
||
+#define FIXED_REGISTERS \
|
||
+ {1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, M_REGS_FIXED, }
|
||
+ /* -, X, Y, U, S, PC,D, Z, A, B, C, DP,FP,AP,M... */
|
||
+
|
||
+/* 1 for registers not available across function calls.
|
||
+ These must include the FIXED_REGISTERS and also any
|
||
+ registers that can be used without being saved.
|
||
+ The latter must include the registers where values are returned
|
||
+ and the register where structure-value addresses are passed.
|
||
+ Aside from that, you can include as many other registers as you like. */
|
||
+#define CALL_USED_REGISTERS \
|
||
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, M_REGS_CALL_USED, }
|
||
+ /* -, X, Y, U, S, PC,D, Z, A, B, C, DP,FP,AP,M... */
|
||
+
|
||
+/* Return number of consecutive hard regs needed starting at reg REGNO
|
||
+ to hold something of mode MODE.
|
||
+ For the 6809, we distinguish between word-length and byte-length
|
||
+ registers. */
|
||
+#define HARD_REGNO_NREGS(REGNO, MODE) \
|
||
+ (REGSET_CONTAINS_P (REGNO, WORD_REGSET) ? \
|
||
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) : \
|
||
+ (GET_MODE_SIZE (MODE)))
|
||
+
|
||
+
|
||
+/* Value is 1 if hard register REGNO can hold a value
|
||
+of machine-mode MODE. */
|
||
+#define HARD_REGNO_MODE_OK(REGNO, MODE) m6809_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. */
|
||
+#define MODES_TIEABLE_P(MODE1, MODE2) 0
|
||
+
|
||
+/* Specify the registers used for certain standard purposes.
|
||
+ The values of these macros are register numbers. */
|
||
+
|
||
+/* program counter if referenced as a register */
|
||
+#define PC_REGNUM HARD_PC_REGNUM
|
||
+
|
||
+/* Register to use for pushing function arguments. */
|
||
+#define STACK_POINTER_REGNUM HARD_S_REGNUM
|
||
+
|
||
+/* Base register for access to local variables of the function.
|
||
+ * Before reload, FRAME_POINTER_REGNUM will be used. Later,
|
||
+ * the elimination pass will convert these to STACK_POINTER_REGNUM
|
||
+ * if possible, or else HARD_FRAME_POINTER_REGNUM. The idea is to
|
||
+ * avoid tying up a hard register (U) for the frame pointer if
|
||
+ * it can be eliminated entirely, making it available for use as
|
||
+ * a general register. */
|
||
+#define FRAME_POINTER_REGNUM SOFT_FP_REGNUM
|
||
+#define HARD_FRAME_POINTER_REGNUM HARD_U_REGNUM
|
||
+
|
||
+/* Define a table of possible eliminations.
|
||
+ * The idea is to try to avoid using hard registers for the argument
|
||
+ * and frame pointers if they can be derived from the stack pointer
|
||
+ * instead, which already has a hard register reserved for it.
|
||
+ *
|
||
+ * The order of entries in this table will try to convert
|
||
+ * ARG_POINTER_REGNUM and FRAME_POINTER_REGNUM into stack pointer
|
||
+ * references first, but if that fails, they will be converted to use
|
||
+ * HARD_FRAME_POINTER_REGNUM.
|
||
+ */
|
||
+#define ELIMINABLE_REGS \
|
||
+{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \
|
||
+ { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \
|
||
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \
|
||
+ { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }}
|
||
+
|
||
+/* #define CAN_ELIMINATE(FROM, TO) m6809_can_eliminate (FROM, TO) */
|
||
+
|
||
+/* Define how to offset the frame or argument pointer to turn it
|
||
+ * into a stack pointer reference. This is based on the way that
|
||
+ * the frame is constructed in the function prologue. */
|
||
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
|
||
+ (OFFSET) = m6809_initial_elimination_offset (FROM, TO)
|
||
+
|
||
+/* Base register for access to arguments of the function.
|
||
+ * This is only used prior to reload; no instructions will ever
|
||
+ * be output referring to this register. */
|
||
+#define ARG_POINTER_REGNUM SOFT_AP_REGNUM
|
||
+
|
||
+/* Register in which static-chain is passed to a function. */
|
||
+#define STATIC_CHAIN_REGNUM HARD_Y_REGNUM
|
||
+
|
||
+/* #define CONDITIONAL_REGISTER_USAGE (m6809_conditional_register_usage ()) */
|
||
+
|
||
+/* Order in which hard registers are allocated to pseudos.
|
||
+ *
|
||
+ * Since the D register is the only valid reg for 8-bit values
|
||
+ * now, avoid using it for 16-bit values by putting it after all
|
||
+ * other 16-bits.
|
||
+ *
|
||
+ * Prefer X first since the first 16-bit function argument goes
|
||
+ * there. We may be able to pass in to a subroutine without
|
||
+ * a copy.
|
||
+ *
|
||
+ * Prefer U over Y since instructions using Y take one extra
|
||
+ * byte, and thus one extra cycle to execute.
|
||
+ */
|
||
+#define REG_ALLOC_ORDER \
|
||
+ { HARD_X_REGNUM, HARD_U_REGNUM, HARD_Y_REGNUM, HARD_D_REGNUM, \
|
||
+ HARD_M_REGNUMS, HARD_S_REGNUM, HARD_PC_REGNUM, \
|
||
+ HARD_B_REGNUM, HARD_A_REGNUM, HARD_CC_REGNUM, \
|
||
+ HARD_DP_REGNUM, SOFT_FP_REGNUM, SOFT_AP_REGNUM, \
|
||
+ 6, HARD_Z_REGNUM }
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ classes of registers
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+/* 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. */
|
||
+
|
||
+enum reg_class {
|
||
+ NO_REGS, /* The trivial class with no registers in it */
|
||
+ D_REGS, /* 16-bit (word (HI)) data (D) */
|
||
+ ACC_A_REGS, /* The A register */
|
||
+ ACC_B_REGS, /* The B register */
|
||
+ X_REGS, /* The X register */
|
||
+ Z_REGS, /* The Z (zero-bit) register */
|
||
+ Q_REGS, /* 8-bit (byte (QI)) data (A,B) */
|
||
+ M_REGS, /* 8-bit (byte (QI)) soft registers */
|
||
+ CC_REGS, /* 8-bit condition code register */
|
||
+ I_REGS, /* An index register (A,B,D) */
|
||
+ T_REGS, /* 16-bit addresses, not including stack or PC (X,Y,U) */
|
||
+ A_REGS, /* 16-bit addresses (X,Y,U,S,PC) */
|
||
+ S_REGS, /* 16-bit soft registers (FP, AP) */
|
||
+ P_REGS, /* 16-bit pushable registers (D,X,Y,U); omit PC and S */
|
||
+ G_REGS, /* 16-bit data and address (D,X,Y,U,S,PC) */
|
||
+ ALL_REGS, /* All registers */
|
||
+ LIM_REG_CLASSES
|
||
+};
|
||
+
|
||
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
|
||
+
|
||
+/* Since GENERAL_REGS is a smaller class than ALL_REGS,
|
||
+ it is not an alias to ALL_REGS, but to G_REGS. */
|
||
+#define GENERAL_REGS G_REGS
|
||
+
|
||
+/* Give names of register classes as strings for dump file. */
|
||
+#define REG_CLASS_NAMES \
|
||
+ { "NO_REGS", "D_REGS", "ACC_A_REGS", "ACC_B_REGS", "X_REGS", "Z_REGS", "Q_REGS", "M_REGS", \
|
||
+ "CC_REGS", "I_REGS", "T_REGS", "A_REGS", "S_REGS", "P_REGS", "G_REGS", \
|
||
+ "ALL_REGS" }
|
||
+
|
||
+/* Define which registers fit in which classes.
|
||
+ This is an initializer for a vector of HARD_REG_SET
|
||
+ of length N_REG_CLASSES. */
|
||
+
|
||
+#define D_REGSET (D_REGBIT)
|
||
+#define ACC_A_REGSET (A_REGBIT)
|
||
+#define ACC_B_REGSET (D_REGBIT)
|
||
+#define X_REGSET (X_REGBIT)
|
||
+#define Z_REGSET (Z_REGBIT)
|
||
+#define Q_REGSET (D_REGBIT | A_REGBIT)
|
||
+#define M_REGSET (SOFT_M_REGBITS)
|
||
+#define CC_REGSET (CC_REGBIT)
|
||
+#define I_REGSET (A_REGBIT | B_REGBIT | D_REGBIT)
|
||
+#define T_REGSET (X_REGBIT | Y_REGBIT | U_REGBIT)
|
||
+#define A_REGSET (X_REGBIT | Y_REGBIT | U_REGBIT | S_REGBIT | PC_REGBIT)
|
||
+#define S_REGSET (SOFT_FP_REGBIT | SOFT_AP_REGBIT)
|
||
+#define P_REGSET (D_REGBIT | X_REGBIT | Y_REGBIT | U_REGBIT)
|
||
+#define G_REGSET \
|
||
+ (D_REGSET | Q_REGSET | I_REGSET | A_REGSET | M_REGSET | S_REGSET)
|
||
+#define ALL_REGSET (G_REGSET)
|
||
+
|
||
+#define REG_CLASS_CONTENTS { \
|
||
+ {0}, \
|
||
+ {D_REGSET}, \
|
||
+ {ACC_A_REGSET}, \
|
||
+ {ACC_B_REGSET}, \
|
||
+ {X_REGSET}, \
|
||
+ {Z_REGSET}, \
|
||
+ {Q_REGSET}, \
|
||
+ {M_REGSET}, \
|
||
+ {CC_REGSET}, \
|
||
+ {I_REGSET}, \
|
||
+ {T_REGSET}, \
|
||
+ {A_REGSET}, \
|
||
+ {S_REGSET}, \
|
||
+ {P_REGSET}, \
|
||
+ {G_REGSET}, \
|
||
+ {ALL_REGSET}, \
|
||
+}
|
||
+
|
||
+/* The same information, inverted.
|
||
+ * This is defined to use the REG_CLASS_CONTENTS defines above, so that
|
||
+ * these two sets of definitions are always consistent. */
|
||
+
|
||
+#define REGNO_REG_CLASS(REGNO) \
|
||
+ (D_REGNO_P (REGNO) ? D_REGS : \
|
||
+ (Z_REGNO_P (REGNO) ? Z_REGS : \
|
||
+ (ACC_A_REGNO_P (REGNO) ? ACC_A_REGS : \
|
||
+ (ACC_B_REGNO_P (REGNO) ? ACC_B_REGS : \
|
||
+ (X_REGNO_P (REGNO) ? X_REGS : \
|
||
+ (Q_REGNO_P (REGNO) ? Q_REGS : \
|
||
+ (M_REGNO_P (REGNO) ? M_REGS : \
|
||
+ (CC_REGNO_P (REGNO) ? CC_REGS : \
|
||
+ (I_REGNO_P (REGNO) ? I_REGS : \
|
||
+ (T_REGNO_P (REGNO) ? T_REGS : \
|
||
+ (A_REGNO_P (REGNO) ? A_REGS : \
|
||
+ (S_REGNO_P (REGNO) ? S_REGS : \
|
||
+ (P_REGNO_P (REGNO) ? P_REGS : \
|
||
+ (G_REGNO_P (REGNO) ? G_REGS : ALL_REGS))))))))))))))
|
||
+
|
||
+#define D_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, D_REGSET))
|
||
+#define ACC_A_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, ACC_A_REGSET))
|
||
+#define ACC_B_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, ACC_B_REGSET))
|
||
+#define X_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, X_REGSET))
|
||
+#define Z_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, Z_REGSET))
|
||
+#define Q_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, Q_REGSET))
|
||
+#define M_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, M_REGSET))
|
||
+#define CC_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, CC_REGSET))
|
||
+#define I_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, I_REGSET))
|
||
+#define T_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, T_REGSET))
|
||
+#define A_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, A_REGSET))
|
||
+#define S_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, S_REGSET))
|
||
+#define P_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, P_REGSET))
|
||
+#define G_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, G_REGSET))
|
||
+
|
||
+/* Macros that test an rtx 'X' to see if it's in a particular
|
||
+ * register class. 'X' need not be a REG necessarily. */
|
||
+
|
||
+#define D_REG_P(X) (REG_P (X) && D_REGNO_P (REGNO (X)))
|
||
+#define ACC_A_REG_P(X) (REG_P (X) && ACC_A_REGNO_P (REGNO (X)))
|
||
+#define ACC_B_REG_P(X) (REG_P (X) && ACC_B_REGNO_P (REGNO (X)))
|
||
+#define X_REG_P(X) (REG_P (X) && X_REGNO_P (REGNO (X)))
|
||
+#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X)))
|
||
+#define I_REG_P(X) (REG_P (X) && I_REGNO_P (REGNO (X)))
|
||
+#define T_REG_P(X) (REG_P (X) && T_REGNO_P (REGNO (X)))
|
||
+#define A_REG_P(X) (REG_P (X) && A_REGNO_P (REGNO (X)))
|
||
+#define S_REG_P(X) (REG_P (X) && S_REGNO_P (REGNO (X)))
|
||
+#define P_REG_P(X) (REG_P (X) && P_REGNO_P (REGNO (X)))
|
||
+#define Q_REG_P(X) (REG_P (X) && Q_REGNO_P (REGNO (X)))
|
||
+#define M_REG_P(X) (REG_P (X) && M_REGNO_P (REGNO (X)))
|
||
+#define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X)))
|
||
+
|
||
+/* Redefine this in terms of BYTE_REGSET */
|
||
+#define BYTE_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, BYTE_REGSET))
|
||
+
|
||
+/* The class value for index registers, and the one for base regs. */
|
||
+#define INDEX_REG_CLASS I_REGS
|
||
+#define BASE_REG_CLASS A_REGS
|
||
+
|
||
+/* Get reg_class from a letter in the machine description. */
|
||
+#define REG_CLASS_FROM_LETTER(C) \
|
||
+ (((C) == 'a' ? A_REGS : \
|
||
+ ((C) == 'd' ? D_REGS : \
|
||
+ ((C) == 'x' ? I_REGS : \
|
||
+ ((C) == 't' ? M_REGS : \
|
||
+ ((C) == 'c' ? CC_REGS : \
|
||
+ ((C) == 'A' ? ACC_A_REGS : \
|
||
+ ((C) == 'B' ? ACC_B_REGS : \
|
||
+ ((C) == 'v' ? X_REGS : \
|
||
+ ((C) == 'u' ? S_REGS : \
|
||
+ ((C) == 'U' ? P_REGS : \
|
||
+ ((C) == 'T' ? T_REGS : \
|
||
+ ((C) == 'z' ? Z_REGS : \
|
||
+ ((C) == 'q' ? Q_REGS : NO_REGS))))))))))))))
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ The letters I through O 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.
|
||
+
|
||
+ For the 6809, J, K, L are used for indexed addressing.
|
||
+ `I' is used for the constant 1.
|
||
+ `J' is used for the 5-bit offsets.
|
||
+ `K' is used for the 8-bit offsets.
|
||
+ `L' is used for the range of signed numbers that fit in 16 bits.
|
||
+ `M' is used for the exact value '8'.
|
||
+ `N' is used for the constant -1.
|
||
+ `O' is used for the constant 0.
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+#define CONST_OK_FOR_LETTER_P(VALUE, C) \
|
||
+ ((C) == 'I' ? ((VALUE) == 1) : \
|
||
+ (C) == 'J' ? ((VALUE) >= -16 && (VALUE) <= 15) : \
|
||
+ (C) == 'K' ? ((VALUE) >= -128 && (VALUE) <= 127) : \
|
||
+ (C) == 'L' ? ((VALUE) >= -32768 && (VALUE) <= 32767) : \
|
||
+ (C) == 'M' ? ((VALUE) == 8) : \
|
||
+ (C) == 'N' ? ((VALUE) == -1) : \
|
||
+ (C) == 'O' ? ((VALUE) == 0) : 0)
|
||
+
|
||
+/* Similar, but for floating constants, and defining letters G and H.
|
||
+ No floating-point constants are valid on MC6809. */
|
||
+#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)
|
||
+
|
||
+/* Given an rtx X being reloaded into a reg required to be
|
||
+ in class CLASS, return the class of reg to actually use.
|
||
+ In general this is just CLASS; but on some machines
|
||
+ in some cases it is preferable to use a more restrictive class. */
|
||
+#define PREFERRED_RELOAD_CLASS(X,CLASS) m6809_preferred_reload_class(X,CLASS)
|
||
+
|
||
+#define SMALL_REGISTER_CLASSES 1
|
||
+
|
||
+/* Return the maximum number of consecutive registers
|
||
+ needed to represent mode MODE in a register of class CLASS. */
|
||
+#define CLASS_MAX_NREGS(CLASS, MODE) \
|
||
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ 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 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 FRAME_GROWS_DOWNWARD 1
|
||
+
|
||
+
|
||
+/* 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
|
||
+
|
||
+
|
||
+/* Always push stack arguments for now. Accumulation is not yet working. */
|
||
+#define PUSH_ROUNDING(BYTES) (BYTES)
|
||
+
|
||
+
|
||
+/* Offset of first parameter from the argument pointer register value.
|
||
+ * ARG_POINTER_REGNUM is defined to point to the return address pushed
|
||
+ * onto the stack, so we must offset by 2 bytes to get to the arguments. */
|
||
+#define FIRST_PARM_OFFSET(FNDECL) 2
|
||
+
|
||
+/* 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. */
|
||
+/* #define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 */
|
||
+
|
||
+/* Define how to find the value returned by a function.
|
||
+ VALTYPE is the data type of the value (as a tree).
|
||
+ If the precise function being called is known, FUNC is its FUNCTION_DECL;
|
||
+ otherwise, FUNC is 0. */
|
||
+#define FUNCTION_VALUE(VALTYPE, FUNC) m6809_function_value (VALTYPE, FUNC)
|
||
+
|
||
+/* Define how to find the value returned by a library function
|
||
+ assuming the value has mode MODE. */
|
||
+
|
||
+/* All return values are in the X-register. */
|
||
+#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, HARD_X_REGNUM)
|
||
+
|
||
+/* Define this if using the nonreentrant convention for returning
|
||
+ structure and union values. No; it is inefficient and buggy. */
|
||
+#undef PCC_STATIC_STRUCT_RETURN
|
||
+
|
||
+/* 1 if N is a possible register number for a function value. */
|
||
+#define FUNCTION_VALUE_REGNO_P(N) m6809_function_value_regno_p (N)
|
||
+
|
||
+/* Define this to be true when FUNCTION_VALUE_REGNO_P is true for
|
||
+ more than one register. */
|
||
+#define NEEDS_UNTYPED_CALL 1
|
||
+
|
||
+/* 1 if N is a possible register number for function argument passing. */
|
||
+#define FUNCTION_ARG_REGNO_P(N) \
|
||
+ ((m6809_abi_version != M6809_ABI_VERSION_STACK) ? \
|
||
+ (((N) == HARD_D_REGNUM) || ((N) == HARD_X_REGNUM)) : \
|
||
+ 0)
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Argument Lists
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+/* Cumulative arguments are tracked in a single integer,
|
||
+ * which is the number of bytes of arguments scanned so far,
|
||
+ * plus which registers have already been used. The register
|
||
+ * info is kept in some of the upper bits */
|
||
+#define CUMULATIVE_ARGS unsigned int
|
||
+
|
||
+#define CUM_STACK_ONLY 0x80000000
|
||
+#define CUM_X_MASK 0x40000000
|
||
+#define CUM_B_MASK 0x20000000
|
||
+#define CUM_STACK_INVALID 0x10000000
|
||
+#define CUM_STACK_MASK 0xFFFFFFF
|
||
+
|
||
+#define CUM_ADVANCE_8BIT(cum) \
|
||
+ (((cum) & CUM_B_MASK) ? (cum)++ : ((cum) |= CUM_B_MASK))
|
||
+
|
||
+#define CUM_ADVANCE_16BIT(cum) \
|
||
+ (((cum) & CUM_X_MASK) ? (cum) += 2 : ((cum) |= CUM_X_MASK))
|
||
+
|
||
+/* 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.
|
||
+ N_NAMED was added in gcc 3.4 and is not used currently. */
|
||
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT,N_NAMED) \
|
||
+ ((CUM) = m6809_init_cumulative_args (CUM, FNTYPE, LIBNAME))
|
||
+
|
||
+#define FUNCTION_ARG_SIZE(MODE, TYPE) \
|
||
+ ((MODE) != BLKmode ? GET_MODE_SIZE (MODE) \
|
||
+ : (unsigned) int_size_in_bytes (TYPE))
|
||
+
|
||
+/* 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) \
|
||
+ (((MODE == QImode) && !((CUM) & CUM_STACK_ONLY)) ? \
|
||
+ CUM_ADVANCE_8BIT (CUM) : \
|
||
+ ((MODE == HImode) && !((CUM) & CUM_STACK_ONLY)) ? \
|
||
+ CUM_ADVANCE_16BIT (CUM) : \
|
||
+ ((CUM) = ((CUM) + (TYPE ? int_size_in_bytes (TYPE) : 2))))
|
||
+
|
||
+/* Define where to put the arguments to a function.
|
||
+ Value is zero to push the argument on the stack,
|
||
+ or a hard register rtx in which to store the argument.
|
||
+ This macro is used _before_ FUNCTION_ARG_ADVANCE.
|
||
+
|
||
+ For the 6809, the first 8-bit function argument can be placed into B,
|
||
+ and the first 16-bit arg can go into X. All other arguments
|
||
+ will be pushed onto the stack.
|
||
+
|
||
+ Command-line options can adjust this behavior somewhat.
|
||
+ */
|
||
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
|
||
+ ((MODE == VOIDmode) ? NULL_RTX : \
|
||
+ ((MODE == BLKmode) || (GET_MODE_SIZE (MODE) > 2)) ? NULL_RTX : \
|
||
+ ((MODE == QImode) && !((CUM) & (CUM_STACK_ONLY | CUM_B_MASK))) ? \
|
||
+ gen_rtx_REG (QImode, HARD_D_REGNUM) : \
|
||
+ ((MODE == HImode) && !((CUM) & (CUM_STACK_ONLY | CUM_X_MASK))) ? \
|
||
+ gen_rtx_REG (HImode, HARD_X_REGNUM) : m6809_function_arg_on_stack (&CUM))
|
||
+
|
||
+/* Output assembler code to FILE to increment profiler label # LABELNO
|
||
+ for profiling a function entry. */
|
||
+#define FUNCTION_PROFILER(FILE, LABELNO) \
|
||
+ fprintf (FILE, "\tldd\t#LP%u\n\tjsr\tmcount\n", (LABELNO));
|
||
+
|
||
+/* Stack pointer must be correct on function exit */
|
||
+#define EXIT_IGNORE_STACK 0
|
||
+
|
||
+/*****************************************************************************
|
||
+**
|
||
+** Trampolines for Nested Functions
|
||
+**
|
||
+*****************************************************************************/
|
||
+
|
||
+/* Length in units of the trampoline for entering a nested function. */
|
||
+#define TRAMPOLINE_SIZE 7
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Addressing modes,
|
||
+ and classification of registers for them.
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+/* 6809 has postincrement and predecrement addressing modes */
|
||
+#define HAVE_POST_INCREMENT 1
|
||
+#define HAVE_PRE_DECREMENT 1
|
||
+
|
||
+/* Whether or not to use index registers is configurable.
|
||
+ * Experiments show that things work better when this is off, so
|
||
+ * that's the way it is for now. */
|
||
+#undef USE_INDEX_REGISTERS
|
||
+
|
||
+
|
||
+/* Macros to check register numbers against specific register classes. */
|
||
+#define REG_VALID_FOR_BASE_P(REGNO) \
|
||
+ (((REGNO) < FIRST_PSEUDO_REGISTER) && A_REGNO_P (REGNO))
|
||
+
|
||
+/* MC6809 index registers do not allow scaling, */
|
||
+/* but there is "accumulator-offset" mode. */
|
||
+#ifdef USE_INDEX_REGISTERS
|
||
+#define REG_VALID_FOR_INDEX_P(REGNO) \
|
||
+ (((REGNO) < FIRST_PSEUDO_REGISTER) && I_REGNO_P (REGNO))
|
||
+#else
|
||
+#define REG_VALID_FOR_INDEX_P(REGNO) 0
|
||
+#endif
|
||
+
|
||
+/* 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) \
|
||
+ ((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) \
|
||
+ ((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_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))
|
||
+
|
||
+#ifndef REG_OK_STRICT
|
||
+#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P(X)
|
||
+#ifdef USE_INDEX_REGISTERS
|
||
+#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P(X)
|
||
+#else
|
||
+#define REG_OK_FOR_INDEX_P(X) 0
|
||
+#endif
|
||
+#else
|
||
+#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P (X)
|
||
+#ifdef USE_INDEX_REGISTERS
|
||
+#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_STRICT_P (X)
|
||
+#else
|
||
+#define REG_OK_FOR_INDEX_P(X) 0
|
||
+#endif
|
||
+#endif
|
||
+
|
||
+/* Maximum number of registers that can appear in a valid memory address */
|
||
+#ifdef USE_INDEX_REGISTERS
|
||
+#define MAX_REGS_PER_ADDRESS 2
|
||
+#else
|
||
+#define MAX_REGS_PER_ADDRESS 1
|
||
+#endif
|
||
+
|
||
+/* 1 if X is an rtx for a constant that is a valid address.
|
||
+ * We allow any constant, plus the sum of any two constants (this allows
|
||
+ * offsetting a symbol ref) */
|
||
+#define CONSTANT_ADDRESS_P(X) \
|
||
+ ((CONSTANT_P (X)) \
|
||
+ || ((GET_CODE (X) == PLUS) \
|
||
+ && (CONSTANT_P (XEXP (X, 0))) && (CONSTANT_P (XEXP (X, 1)))))
|
||
+
|
||
+/* Nonzero if the constant value X is a legitimate general operand.
|
||
+ It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
||
+/* Any single-word constant is ok; the only contexts
|
||
+ allowing general_operand of mode DI or DF are movdi and movdf. */
|
||
+#define LEGITIMATE_CONSTANT_P(X) (GET_CODE (X) != CONST_DOUBLE)
|
||
+
|
||
+/* Nonzero if the X is a legitimate immediate operand in PIC mode. */
|
||
+#define LEGITIMATE_PIC_OPERAND_P(X) !symbolic_operand (X, VOIDmode)
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Test for valid memory addresses
|
||
+--------------------------------------------------------------*/
|
||
+/* 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
|
||
+ constant indexed N,X
|
||
+ accumulator indexed D,X
|
||
+ auto_increment ,X++
|
||
+ auto_decrement ,--X
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+#define REGISTER_ADDRESS_P(X) \
|
||
+ (REG_P (X) && REG_OK_FOR_BASE_P (X))
|
||
+
|
||
+#define EXTENDED_ADDRESS_P(X) \
|
||
+ CONSTANT_ADDRESS_P (X) \
|
||
+
|
||
+#define LEGITIMATE_BASE_P(X) \
|
||
+ ((REG_P (X) && REG_OK_FOR_BASE_P (X)) \
|
||
+ || (GET_CODE (X) == SIGN_EXTEND \
|
||
+ && GET_CODE (XEXP (X, 0)) == REG \
|
||
+ && GET_MODE (XEXP (X, 0)) == HImode \
|
||
+ && REG_OK_FOR_BASE_P (XEXP (X, 0))))
|
||
+
|
||
+#define LEGITIMATE_OFFSET_P(X) \
|
||
+ (CONSTANT_ADDRESS_P (X) || (REG_P (X) && REG_OK_FOR_INDEX_P (X)))
|
||
+
|
||
+/* 1 if X is the sum of a base register and an offset. */
|
||
+#define INDEXED_ADDRESS(X) \
|
||
+ ((GET_CODE (X) == PLUS \
|
||
+ && LEGITIMATE_BASE_P (XEXP (X, 0)) \
|
||
+ && LEGITIMATE_OFFSET_P (XEXP (X, 1))) \
|
||
+ || (GET_CODE (X) == PLUS \
|
||
+ && LEGITIMATE_BASE_P (XEXP (X, 1)) \
|
||
+ && LEGITIMATE_OFFSET_P (XEXP (X, 0))))
|
||
+
|
||
+#define STACK_REG_P(X) (REG_P(X) && REGNO(X) == HARD_S_REGNUM)
|
||
+
|
||
+#define STACK_PUSH_P(X) \
|
||
+ (MEM_P (X) && GET_CODE (XEXP (X, 0)) == PRE_DEC && STACK_REG_P (XEXP (XEXP (X, 0), 0)))
|
||
+
|
||
+#define STACK_POP_P(X) \
|
||
+ (MEM_P (X) && GET_CODE (XEXP (X, 0)) == POST_INC && STACK_REG_P (XEXP (XEXP (X, 0), 0)))
|
||
+
|
||
+#define PUSH_POP_ADDRESS_P(X) \
|
||
+ (((GET_CODE (X) == PRE_DEC) || (GET_CODE (X) == POST_INC)) \
|
||
+ && (LEGITIMATE_BASE_P (XEXP (X, 0))))
|
||
+
|
||
+/* Go to ADDR if X is a valid address. */
|
||
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
|
||
+{ \
|
||
+ if (REGISTER_ADDRESS_P(X)) goto ADDR; \
|
||
+ if (PUSH_POP_ADDRESS_P (X)) goto ADDR; \
|
||
+ if (EXTENDED_ADDRESS_P (X)) goto ADDR; \
|
||
+ if (INDEXED_ADDRESS (X)) goto ADDR; \
|
||
+ if (MEM_P (X) && REGISTER_ADDRESS_P(XEXP (X, 0))) goto ADDR; \
|
||
+ if (MEM_P (X) && PUSH_POP_ADDRESS_P (XEXP (X, 0))) goto ADDR; \
|
||
+ if (MEM_P (X) && EXTENDED_ADDRESS_P (XEXP (X, 0))) goto ADDR; \
|
||
+ if (MEM_P (X) && INDEXED_ADDRESS (XEXP (X, 0))) goto ADDR; \
|
||
+}
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Address Fix-up
|
||
+--------------------------------------------------------------*/
|
||
+/* Go to LABEL if ADDR (a legitimate address expression)
|
||
+ has an effect that depends on the machine mode it is used for.
|
||
+ In the latest GCC, this case is already handled by the core code
|
||
+ so no action is required here. */
|
||
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {}
|
||
+
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ Miscellaneous Parameters
|
||
+--------------------------------------------------------------*/
|
||
+/* Specify the machine mode that this machine uses
|
||
+ for the index in the tablejump instruction. */
|
||
+#define CASE_VECTOR_MODE Pmode
|
||
+
|
||
+/* Define this as 1 if `char' should by default be signed; else as 0. */
|
||
+#define DEFAULT_SIGNED_CHAR 0
|
||
+
|
||
+/* 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/register
|
||
+ in one reasonably fast instruction. */
|
||
+#define MOVE_MAX 2
|
||
+
|
||
+/* Int can be 8 or 16 bits (default is 16) */
|
||
+#define INT_TYPE_SIZE (TARGET_BYTE_INT ? 8 : 16)
|
||
+
|
||
+/* Short is always 16 bits */
|
||
+#define SHORT_TYPE_SIZE (TARGET_BYTE_INT ? 8 : 16)
|
||
+
|
||
+/* Size (bits) of the type "long" on target machine */
|
||
+#define LONG_TYPE_SIZE (TARGET_BYTE_INT ? 16 : 32)
|
||
+
|
||
+/* Size (bits) of the type "long long" on target machine */
|
||
+#define LONG_LONG_TYPE_SIZE 32
|
||
+
|
||
+/* Size (bits) of the type "char" on target machine */
|
||
+#define CHAR_TYPE_SIZE 8
|
||
+
|
||
+/* Size (bits) of the type "float" on target machine */
|
||
+#define FLOAT_TYPE_SIZE 32
|
||
+
|
||
+/* Size (bits) of the type "double" on target machine.
|
||
+ * Note that the C standard does not require that doubles
|
||
+ * hold any more bits than float. Since the 6809 has so few
|
||
+ * registers, we cannot really support more than 32-bits. */
|
||
+#define DOUBLE_TYPE_SIZE 32
|
||
+
|
||
+/* Size (bits) of the type "long double" on target machine */
|
||
+#define LONG_DOUBLE_TYPE_SIZE 32
|
||
+
|
||
+/* Define the type used for "size_t". With a 64KB address space,
|
||
+ * only a 16-bit value here makes sense. */
|
||
+#define SIZE_TYPE (TARGET_BYTE_INT ? "long unsigned int" : "unsigned int")
|
||
+
|
||
+/* Likewise, the difference between two pointers is also a 16-bit
|
||
+ * signed value. */
|
||
+#define PTRDIFF_TYPE (TARGET_BYTE_INT ? "long int" : "int")
|
||
+
|
||
+/* Nonzero if access to memory by bytes is slow and undesirable. */
|
||
+#define SLOW_BYTE_ACCESS 0
|
||
+
|
||
+/* 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 0
|
||
+
|
||
+/* 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
|
||
+
|
||
+/* It is as good to call a constant function address as to
|
||
+ call an address kept in a register. */
|
||
+#define NO_FUNCTION_CSE
|
||
+
|
||
+/* 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 HImode
|
||
+
|
||
+/* Define the cost of moving a value from a register in CLASS1
|
||
+ * to CLASS2, of a given MODE.
|
||
+ *
|
||
+ * On the 6809, hard register transfers are all basically equivalent.
|
||
+ * But soft register moves are treated more like memory moves. */
|
||
+#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
|
||
+ (((CLASS1 == M_REGS) || (CLASS2 == M_REGS)) ? 4 : 7)
|
||
+
|
||
+/* Define the cost of moving a value between a register and memory. */
|
||
+#define MEMORY_MOVE_COST(MODE, CLASS, IN) 5
|
||
+
|
||
+/* Check a `double' value for validity for a particular machine mode. */
|
||
+
|
||
+#define CHECK_FLOAT_VALUE(MODE, D, OVERFLOW) \
|
||
+ ((OVERFLOW) = check_float_value (MODE, &D, OVERFLOW))
|
||
+
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ machine-dependent
|
||
+--------------------------------------------------------------*/
|
||
+/* Tell final.c how to eliminate redundant test instructions. */
|
||
+
|
||
+/* Here we define machine-dependent flags and fields in cc_status
|
||
+ (see `conditions.h'). */
|
||
+
|
||
+/* 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. */
|
||
+
|
||
+/* On the 6809, most of the insns to store in an address register
|
||
+ fail to set the cc's. However, in some cases these instructions
|
||
+ can make it possibly invalid to use the saved cc's. In those
|
||
+ cases we clear out some or all of the saved cc's so they won't be used. */
|
||
+
|
||
+#define NOTICE_UPDATE_CC(EXP, INSN) \
|
||
+ notice_update_cc((EXP), (INSN))
|
||
+
|
||
+/*****************************************************************************
|
||
+**
|
||
+** pragma support
|
||
+**
|
||
+*****************************************************************************/
|
||
+
|
||
+#if 0
|
||
+#define REGISTER_TARGET_PRAGMAS() \
|
||
+do { \
|
||
+ extern void pragma_section PARAMS ((cpp_reader *)); \
|
||
+ c_register_pragma (0, "section", pragma_section); \
|
||
+} while (0)
|
||
+
|
||
+#endif
|
||
+
|
||
+/*--------------------------------------------------------------
|
||
+ ASSEMBLER FORMAT
|
||
+--------------------------------------------------------------*/
|
||
+
|
||
+#define FMT_HOST_WIDE_INT "%ld"
|
||
+
|
||
+/* Output to assembler file text saying following lines
|
||
+ may contain character constants, extra white space, comments, etc. */
|
||
+#define ASM_APP_ON ";----- asm -----\n"
|
||
+
|
||
+/* Output to assembler file text saying following lines
|
||
+ no longer contain unusual constructs. */
|
||
+#define ASM_APP_OFF ";--- end asm ---\n"
|
||
+
|
||
+/* Use a semicolon to begin a comment. */
|
||
+#define ASM_COMMENT_START "; "
|
||
+
|
||
+/* Output assembly directives to switch to section 'name' */
|
||
+#undef TARGET_ASM_NAMED_SECTION
|
||
+#define TARGET_ASM_NAMED_SECTION m6809_asm_named_section
|
||
+
|
||
+#undef TARGET_HAVE_NAMED_SECTION
|
||
+#define TARGET_HAVE_NAMED_SECTION m6809_have_named_section
|
||
+
|
||
+/* Output before read-only data. */
|
||
+#define TEXT_SECTION_ASM_OP (code_section_op)
|
||
+
|
||
+/* Output before writable data. */
|
||
+#define DATA_SECTION_ASM_OP (data_section_op)
|
||
+
|
||
+/* Output before uninitialized data. */
|
||
+#define BSS_SECTION_ASM_OP (bss_section_op)
|
||
+
|
||
+/* Support the ctors and dtors sections for g++. */
|
||
+
|
||
+#undef CTORS_SECTION_ASM_OP
|
||
+#define CTORS_SECTION_ASM_OP "\t.area .ctors"
|
||
+#undef DTORS_SECTION_ASM_OP
|
||
+#define DTORS_SECTION_ASM_OP "\t.area .dtors"
|
||
+
|
||
+
|
||
+#undef DO_GLOBAL_CTORS_BODY
|
||
+#undef DO_GLOBAL_DTORS_BODY
|
||
+
|
||
+#define HAS_INIT_SECTION
|
||
+
|
||
+/* 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) \
|
||
+ if ((LOG) > 1) \
|
||
+ fprintf (FILE, "\t.bndry %u\n", 1 << (LOG))
|
||
+
|
||
+/* The .set foo,bar construct doesn't work by default */
|
||
+#undef SET_ASM_OP
|
||
+#define ASM_OUTPUT_DEF(FILE, LABEL1, LABEL2) \
|
||
+ do \
|
||
+ { \
|
||
+ fputc ('\t', FILE); \
|
||
+ assemble_name (FILE, LABEL1); \
|
||
+ fputs (" = ", FILE); \
|
||
+ assemble_name (FILE, LABEL2); \
|
||
+ fputc ('\n', FILE); \
|
||
+ } \
|
||
+ while (0)
|
||
+
|
||
+/* How to refer to registers in assembler output.
|
||
+ This sequence is indexed by compiler's hard-register-number (see above). */
|
||
+#define MNAME(x) [SOFT_M0_REGNUM+(x)] = "*m" C_STRING(x) ,
|
||
+
|
||
+#define REGISTER_NAMES { \
|
||
+ [HARD_D_REGNUM]= "d", \
|
||
+ [HARD_X_REGNUM]= "x", \
|
||
+ [HARD_Y_REGNUM]= "y", \
|
||
+ [HARD_U_REGNUM]= "u", \
|
||
+ [HARD_S_REGNUM]= "s", \
|
||
+ [HARD_PC_REGNUM]= "pc", \
|
||
+ [HARD_A_REGNUM]= "a", \
|
||
+ [HARD_B_REGNUM]= "b", \
|
||
+ [HARD_CC_REGNUM]= "cc",\
|
||
+ [HARD_DP_REGNUM]= "dp", \
|
||
+ [SOFT_FP_REGNUM]= "soft_fp", \
|
||
+ [SOFT_AP_REGNUM]= "soft_ap", \
|
||
+ MNAME(0) MNAME(1) MNAME(2) MNAME(3) \
|
||
+ MNAME(4) MNAME(5) MNAME(6) MNAME(7) \
|
||
+ [HARD_RSVD1_REGNUM] = "-", \
|
||
+ [HARD_Z_REGNUM] = "z" /* bit 2 of CC */ }
|
||
+
|
||
+/*****************************************************************************
|
||
+**
|
||
+** Debug Support
|
||
+**
|
||
+*****************************************************************************/
|
||
+
|
||
+/* Default to DBX-style debugging */
|
||
+#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG
|
||
+
|
||
+#define DBX_DEBUGGING_INFO
|
||
+
|
||
+#define DEFAULT_GDB_EXTENSIONS 0
|
||
+
|
||
+#define ASM_STABS_OP ";\t.stabs\t"
|
||
+#define ASM_STABD_OP ";\t.stabd\t"
|
||
+#define ASM_STABN_OP ";\t.stabn\t"
|
||
+
|
||
+#define DBX_CONTIN_LENGTH 54
|
||
+
|
||
+#define DBX_OUTPUT_MAIN_SOURCE_FILENAME(ASMFILE, FILENAME) \
|
||
+do { \
|
||
+ const char *p = FILENAME; \
|
||
+ while ((p = strchr (p, '/')) != NULL) { \
|
||
+ p = FILENAME = p+1; \
|
||
+ } \
|
||
+ fprintf (ASMFILE, "%s", ASM_STABS_OP); \
|
||
+ output_quoted_string (ASMFILE, FILENAME); \
|
||
+ fprintf (ASMFILE, ",%d,0,0,", N_SO); \
|
||
+ assemble_name (ASMFILE, ltext_label_name); \
|
||
+ fputc ('\n', ASMFILE); \
|
||
+ switch_to_section (text_section); \
|
||
+ (*targetm.asm_out.internal_label) (ASMFILE, "Ltext", 0); \
|
||
+} while (0)
|
||
+
|
||
+/* With -g, GCC sometimes outputs string literals that are longer than
|
||
+ * the assembler can handle. Without actual debug support, these are
|
||
+ * not really required. Redefine the function to output strings to
|
||
+ * output as much as possible. */
|
||
+#define OUTPUT_QUOTED_STRING(FILE, STR) m6809_output_quoted_string (FILE, STR)
|
||
+
|
||
+/*****************************************************************************
|
||
+**
|
||
+** Output and Generation of Labels
|
||
+**
|
||
+*****************************************************************************/
|
||
+
|
||
+/* Prefixes for various assembly-time objects */
|
||
+
|
||
+#define REGISTER_PREFIX ""
|
||
+
|
||
+#define LOCAL_LABEL_PREFIX ""
|
||
+
|
||
+#define USER_LABEL_PREFIX "_"
|
||
+
|
||
+#define IMMEDIATE_PREFIX "#"
|
||
+
|
||
+/* This is how to output the definition of a user-level label named NAME,
|
||
+ such as the label on a static function or variable NAME. */
|
||
+
|
||
+#define ASM_OUTPUT_LABEL(FILE,NAME) \
|
||
+do { \
|
||
+ if (section_changed) { \
|
||
+ fprintf (FILE, "\n%s\n\n", code_section_op); \
|
||
+ section_changed = 0; \
|
||
+ } \
|
||
+ assemble_name (FILE, NAME); \
|
||
+ fputs (":\n", FILE); \
|
||
+} while (0)
|
||
+
|
||
+/* This is how to output the label for a function definition. It
|
||
+ invokes ASM_OUTPUT_LABEL, but may examine the DECL tree node for
|
||
+ other properties. */
|
||
+#define ASM_DECLARE_FUNCTION_NAME(FILE,NAME,DECL) \
|
||
+ m6809_declare_function_name (FILE,NAME,DECL)
|
||
+
|
||
+/* This is how to output a command to make the user-level label
|
||
+ named NAME defined for reference from other files. */
|
||
+
|
||
+#define GLOBAL_ASM_OP "\t.globl "
|
||
+
|
||
+/* This is how to output a reference to a user label named NAME. */
|
||
+#define ASM_OUTPUT_LABELREF(FILE,NAME) \
|
||
+ fprintf (FILE, "_%s", NAME)
|
||
+
|
||
+/* This is how to output a reference to a symbol ref
|
||
+ * Check to see if the symbol is in the direct page */
|
||
+#define ASM_OUTPUT_SYMBOL_REF(FILE,sym) \
|
||
+{ \
|
||
+ print_direct_prefix (FILE, sym); \
|
||
+ assemble_name (FILE, XSTR (sym, 0)); \
|
||
+}
|
||
+
|
||
+/* External references aren't necessary, so don't emit anything */
|
||
+#define ASM_OUTPUT_EXTERNAL(FILE,DECL,NAME)
|
||
+
|
||
+/* This is how to store into the string LABEL
|
||
+ the symbol_ref name of an internal numbered label where
|
||
+ PREFIX is the class of label and NUM is the number within the class.
|
||
+ This is suitable for output with `assemble_name'. */
|
||
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \
|
||
+ sprintf (LABEL, "*%s%lu", PREFIX, (unsigned long int)NUM)
|
||
+
|
||
+/* This is how to output an assembler line defining an `int' constant. */
|
||
+#define ASM_OUTPUT_INT(FILE,VALUE) \
|
||
+( fprintf (FILE, "\t.word "), \
|
||
+ output_addr_const (FILE, (VALUE)), \
|
||
+ fprintf (FILE, "\n"))
|
||
+
|
||
+/* Likewise for `char' and `short' constants. */
|
||
+#define ASM_OUTPUT_SHORT(FILE,VALUE) \
|
||
+( fprintf (FILE, "\t.word "), \
|
||
+ output_addr_const (FILE, (VALUE)), \
|
||
+ fprintf (FILE, "\n"))
|
||
+
|
||
+/* This is how to output a string. */
|
||
+#define ASM_OUTPUT_ASCII(FILE,STR,SIZE) m6809_output_ascii (FILE, STR, SIZE)
|
||
+
|
||
+/* This is how to output an insn to push a register on the stack.
|
||
+ It need not be very fast code. */
|
||
+
|
||
+#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \
|
||
+ fprintf (FILE, "\tpshs\t%s\n", reg_names[REGNO])
|
||
+
|
||
+/* This is how to output an insn to pop a register from the stack.
|
||
+ It need not be very fast code. */
|
||
+
|
||
+#define ASM_OUTPUT_REG_POP(FILE,REGNO) \
|
||
+ fprintf (FILE, "\tpuls\t%s\n", reg_names[REGNO])
|
||
+
|
||
+/* 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.word L%u\n", VALUE)
|
||
+
|
||
+/* 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.word L%u-L%u\n", VALUE, REL)
|
||
+
|
||
+
|
||
+/*****************************************************************************
|
||
+**
|
||
+** Assembler Commands for Alignment
|
||
+**
|
||
+*****************************************************************************/
|
||
+
|
||
+/* ASM_OUTPUT_SKIP is supposed to zero initialize the data.
|
||
+ * So use the .byte and .word directives instead of .blkb */
|
||
+#define ASM_OUTPUT_SKIP(FILE,SIZE) \
|
||
+ do { \
|
||
+ int __size = SIZE; \
|
||
+ while (__size > 0) { \
|
||
+ if (__size >= 2) \
|
||
+ { \
|
||
+ fprintf (FILE, "\t.word\t0\t;skip space %d\n", __size); \
|
||
+ __size -= 2; \
|
||
+ } \
|
||
+ else \
|
||
+ { \
|
||
+ fprintf (FILE, "\t.byte\t0\t;skip space\n"); \
|
||
+ __size--; \
|
||
+ } \
|
||
+ } \
|
||
+ } while (0)
|
||
+
|
||
+/* This says how to output an assembler line
|
||
+ to define a global common symbol. */
|
||
+
|
||
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
|
||
+ do { \
|
||
+ switch_to_section (bss_section); \
|
||
+ fputs ("\t.globl\t", FILE); \
|
||
+ assemble_name ((FILE), (NAME)); \
|
||
+ fputs ("\n", FILE); \
|
||
+ assemble_name ((FILE), (NAME)); \
|
||
+ fprintf ((FILE), ":\t.blkb\t" FMT_HOST_WIDE_INT "\n", (ROUNDED));} while(0)
|
||
+
|
||
+/* This says how to output an assembler line
|
||
+ to define a local common symbol. */
|
||
+
|
||
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
|
||
+do { \
|
||
+ switch_to_section (bss_section); \
|
||
+ assemble_name ((FILE), (NAME)); \
|
||
+ fprintf ((FILE), ":\t.blkb\t" FMT_HOST_WIDE_INT "\n", (ROUNDED));} while(0)
|
||
+
|
||
+/* Store in OUTPUT a string (made with alloca) containing
|
||
+ an assembler-name for a local static variable named NAME.
|
||
+ LABELNO is an integer which is different for each call. */
|
||
+
|
||
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
|
||
+( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \
|
||
+ sprintf ((OUTPUT), "%s.%lu", (NAME), (unsigned long int)(LABELNO)))
|
||
+
|
||
+/* 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)
|
||
+
|
||
+/* Don't let stack pushes build up too much. */
|
||
+#define MAX_PENDING_STACK 8
|
||
+
|
||
+/* Define values for builtin operations */
|
||
+enum m6809_builtins
|
||
+{
|
||
+ M6809_SWI,
|
||
+ M6809_SWI2,
|
||
+ M6809_SWI3,
|
||
+ M6809_CWAI,
|
||
+ M6809_SYNC,
|
||
+ M6809_ADD_CARRY,
|
||
+ M6809_SUB_CARRY,
|
||
+ M6809_ADD_DECIMAL,
|
||
+ M6809_NOP,
|
||
+ M6809_BLOCKAGE
|
||
+};
|
||
+
|
||
diff --git a/gcc/config/m6809/m6809.md b/gcc/config/m6809/m6809.md
|
||
new file mode 100644
|
||
index 0000000..263aa0f
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/m6809.md
|
||
@@ -0,0 +1,2358 @@
|
||
+;; GCC machine description for Motorola 6809
|
||
+;; Copyright (C) 1989, 2005, 2006, 2007, 2008,
|
||
+;; 2009 Free Software Foundation, Inc.
|
||
+;;
|
||
+;; Mostly by Brian Dominy (brian@oddchange.com) with substantial renovations
|
||
+;; by William Astle (lost@l-w.ca).
|
||
+;;
|
||
+;; Based on earlier work by Tom Jones (jones@sal.wisc.edu) and
|
||
+;; Matthias Doerfel (msdoerfe@informatik.uni-erlangen.de)
|
||
+;;
|
||
+;; 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/>.
|
||
+;;
|
||
+;; General information:
|
||
+;; --------------------
|
||
+;; * This backend is mostly a rewrite from earlier (3.1.1 and before)
|
||
+;; versions.
|
||
+;;
|
||
+;; * The 'A' and 'B' registers are treated as a single register by the
|
||
+;; register allocator; hence, the instruction templates assume that
|
||
+;; both can be modified if either one is available for use. No
|
||
+;; attempt is made to split instructions to refer to a particular half
|
||
+;; of the register. It is always referred to as the 'D' register, even
|
||
+;; in QImode (when it will be displayed as 'B').
|
||
+;;
|
||
+;; * There is full support for proper branch instruction generation,
|
||
+;; based on instruction lengths. However, many instruction patterns
|
||
+;; are still overloaded to emit lots of real instructions, which can
|
||
+;; make the length calculation difficult; in those cases, I've tried
|
||
+;; to be pessimistic and assume the worst-case.
|
||
+;;
|
||
+;; * The instruction type attributes are only defined for branch
|
||
+;; vs. non branch instructions for now, since there is seemingly no
|
||
+;; reason to define these for other types anyway.
|
||
+;;
|
||
+;; * The limited number of total registers presents the greatest
|
||
+;; challenge. There are 'soft registers' -- memory locations
|
||
+;; used to simulate real regs -- which can be helpful.
|
||
+;;
|
||
+;; * Position-independent code (PIC) is supported and has been tested
|
||
+;; but not to the extent of absolute code generation.
|
||
+;;
|
||
+;; * All of the 6809 special opcodes, e.g. SWI and SYNC, are defined
|
||
+;; as UNSPEC instructions, and can be accessed from C code using
|
||
+;; __builtin_xxxx() style functions.
|
||
+;;
|
||
+;; What still needs to be done:
|
||
+;; ----------------------------
|
||
+;; * Replace remaining instances of (define_peephole) with
|
||
+;; (define_peephole2), or remove them completely if they are not
|
||
+;; matching anyway. Add more peepholes for things actually encountered.
|
||
+;;
|
||
+;; * Indexing addressing can lead to crashes in complex functions when
|
||
+;; register pressure is high. Only the 'D' register can actually be
|
||
+;; used as an index register, and its demand by other instructions
|
||
+;; can sometimes mean that it is impossible to satisfy constraints.
|
||
+;; Currently, indexing is completely disabled to avoid these types
|
||
+;; of problems, although code is slightly more inefficient in some
|
||
+;; working cases.
|
||
+;;
|
||
+;; * 32-bit math is terribly inefficient.
|
||
+;;
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Constants
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;
|
||
+; Define constants for hard register numbers.
|
||
+;
|
||
+(define_constants [
|
||
+ (HARD_RSVD1_REGNUM 0)
|
||
+ (HARD_X_REGNUM 1) (HARD_Y_REGNUM 2) (HARD_U_REGNUM 3)
|
||
+ (HARD_S_REGNUM 4) (HARD_PC_REGNUM 5) (HARD_D_REGNUM 6)
|
||
+ (HARD_Z_REGNUM 7)
|
||
+ (HARD_A_REGNUM 8) (HARD_B_REGNUM 9)
|
||
+ (HARD_CC_REGNUM 10) (HARD_DP_REGNUM 11)
|
||
+ (SOFT_FP_REGNUM 12) (SOFT_AP_REGNUM 13)
|
||
+ (SOFT_M0_REGNUM 14) (SOFT_M1_REGNUM 15)
|
||
+ (SOFT_M2_REGNUM 16) (SOFT_M3_REGNUM 17)
|
||
+])
|
||
+
|
||
+
|
||
+;
|
||
+; The range in which a short branch insn can be used.
|
||
+;
|
||
+(define_constants [
|
||
+ (MIN_SHORT_BRANCH_OFFSET -127)
|
||
+ (MAX_SHORT_BRANCH_OFFSET 128)
|
||
+])
|
||
+
|
||
+
|
||
+;
|
||
+; The lengths of various types of real 6809 instructions.
|
||
+;
|
||
+; By default, ordinary insns are 4 bytes long. This is often not
|
||
+; right, and the insn patterns below will redefine this to the
|
||
+; correct value.
|
||
+;
|
||
+; Branch instruction lengths (conditional and unconditionals) are
|
||
+; well known and declared here. The short insns are used when the
|
||
+; offset is within the range declared above (between MIN_SHORT
|
||
+; and MAX_SHORT) ; otherwise the long form is used.
|
||
+;
|
||
+(define_constants [
|
||
+ (DEFAULT_INSN_LENGTH 4)
|
||
+ (SHORT_CBRANCH_LENGTH 2)
|
||
+ (LONG_CBRANCH_LENGTH 4)
|
||
+ (SHORT_BRANCH_LENGTH 2)
|
||
+ (LONG_BRANCH_LENGTH 3)
|
||
+])
|
||
+
|
||
+
|
||
+;
|
||
+; Constants for insn cycle counts.
|
||
+; Note that these counts all assume 1-byte opcodes. 2-byte
|
||
+; opcodes require 1 extra cycles for fetching the extra byte.
|
||
+;
|
||
+(define_constants [
|
||
+ ;; The default insn length, when it cannot be calculated.
|
||
+ ;; Take a conservative approach and estimate high.
|
||
+ (DEFAULT_INSN_CYCLES 10)
|
||
+
|
||
+ ;; Cycle counts for ALU and load operations.
|
||
+ (ALU_INHERENT_CYCLES 2)
|
||
+ (ALU_IMMED_CYCLES 2)
|
||
+ (ALU_DIRECT_CYCLES 4)
|
||
+ (ALU_INDEXED_BASE_CYCLES 4)
|
||
+ (ALU_EXTENDED_CYCLES 5)
|
||
+
|
||
+ ;; If an ALU operation is on a 16-bit register (D), then
|
||
+ ;; add this number of cycles to the total count.
|
||
+ (ALU_16BIT_CYCLES 2)
|
||
+
|
||
+ ;; A load of a 16-bit register incurs this extra amount.
|
||
+ (LOAD_16BIT_CYCLES 1)
|
||
+
|
||
+ ;; Cycle counts for memory-only operations (bit shifts, clear, test)
|
||
+ (MEM_DIRECT_CYCLES 6)
|
||
+ (MEM_INDEXED_BASE_CYCLES 6)
|
||
+ (MEM_EXTENDED_CYCLES 7)
|
||
+
|
||
+ ;; Cycle count for any reg-reg transfer (regardless of size)
|
||
+ (EXG_CYCLES 8)
|
||
+ (TFR_CYCLES 6)
|
||
+
|
||
+ ;; Cycle count for a condition code update (andcc/orcc)
|
||
+ (CC_CYCLES 3)
|
||
+
|
||
+ (JMP_DIRECT_CYCLES 3)
|
||
+ (JMP_INDEXED_BASE_CYCLES 3)
|
||
+ (JMP_EXTENDED_CYCLES 4)
|
||
+
|
||
+ (JSR_DIRECT_CYCLES 7)
|
||
+ (JSR_INDEXED_BASE_CYCLES 7)
|
||
+ (JSR_EXTENDED_CYCLES 8)
|
||
+
|
||
+ (LEA_BASE_CYCLES 4)
|
||
+
|
||
+ ;; Cycle count for a psh/pul operations. Add to this the
|
||
+ ;; total number of bytes moved for the correct count.
|
||
+ (PSH_PUL_CYCLES 5)
|
||
+
|
||
+ ;; Miscellaneous cycle counts
|
||
+ (CWAI_CYCLES 20)
|
||
+ (MUL_CYCLES 11)
|
||
+ (NOP_CYCLES 2)
|
||
+ (RTI_CYCLES 15)
|
||
+ (RTS_CYCLES 5)
|
||
+ (SWI_CYCLES 20)
|
||
+ (SYNC_CYCLES 4)
|
||
+])
|
||
+
|
||
+
|
||
+;
|
||
+; An enumeration of values for each "unspec"; i.e. unspecified
|
||
+; instruction. These represent insns that are meaningful on the
|
||
+; 6809 but which have no intrinsic meaning to GCC itself.
|
||
+; These insns can be generated explicitly using the __builtin_xxx
|
||
+; syntax; they are also implicitly generated by the backend
|
||
+; as needed to implement other insns.
|
||
+;
|
||
+(define_constants [
|
||
+ (UNSPEC_BLOCKAGE 0)
|
||
+ (UNSPEC_PUSH_RS 1)
|
||
+ (UNSPEC_POP_RS 2)
|
||
+ (UNSPEC_SWI 3)
|
||
+ (UNSPEC_CWAI 4)
|
||
+ (UNSPEC_ADD_CARRY 5)
|
||
+ (UNSPEC_SUB_CARRY 6)
|
||
+ (UNSPEC_SYNC 7)
|
||
+ (UNSPEC_ADD_DECIMAL 8)
|
||
+])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Predicates
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(include "predicates.md")
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Attributes
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;;
|
||
+;; The type attribute is used to distinguish between different
|
||
+;; types of branch instructions, so that their lengths can be
|
||
+;; calculated correctly.
|
||
+;;
|
||
+(define_attr "type" "branch,cbranch,unknown" (const_string "unknown"))
|
||
+
|
||
+;;
|
||
+;; The length of a branch instruction is calculated based on how
|
||
+;; far away the branch target is. Lengths of other insns default
|
||
+;; to 4. set_attr is used in instruction templates to specify
|
||
+;; the length when it is known exactly. When not sure, err on
|
||
+;; the high side to avoid compile errors.
|
||
+;;
|
||
+(define_attr "length" ""
|
||
+ (cond [
|
||
+ (eq_attr "type" "branch")
|
||
+ (if_then_else (lt (minus (match_dup 0) (pc))
|
||
+ (const_int MIN_SHORT_BRANCH_OFFSET))
|
||
+ (const_int LONG_BRANCH_LENGTH)
|
||
+ (if_then_else (gt (minus (match_dup 0) (pc))
|
||
+ (const_int MAX_SHORT_BRANCH_OFFSET))
|
||
+ (const_int LONG_BRANCH_LENGTH)
|
||
+ (const_int SHORT_BRANCH_LENGTH)))
|
||
+ (eq_attr "type" "cbranch")
|
||
+ (if_then_else (lt (minus (match_dup 0) (pc))
|
||
+ (const_int MIN_SHORT_BRANCH_OFFSET))
|
||
+ (const_int LONG_CBRANCH_LENGTH)
|
||
+ (if_then_else (gt (minus (match_dup 0) (pc))
|
||
+ (const_int MAX_SHORT_BRANCH_OFFSET))
|
||
+ (const_int LONG_CBRANCH_LENGTH)
|
||
+ (const_int SHORT_CBRANCH_LENGTH)))
|
||
+ ] (const_int DEFAULT_INSN_LENGTH)))
|
||
+
|
||
+
|
||
+;;
|
||
+;; The default attributes for 'asm' statements.
|
||
+;; The default length is the longest possible single 6809 instruction,
|
||
+;; which is 5 bytes. GCC will automatically multiply this by the
|
||
+;; number of real insns contained in an asm statement.
|
||
+;;
|
||
+(define_asm_attributes
|
||
+ [(set_attr "length" "5")
|
||
+ (set_attr "type" "unknown")])
|
||
+
|
||
+;;
|
||
+;; An attribute for the number of cycles that it takes an instruction
|
||
+;; to execute.
|
||
+;;
|
||
+(define_attr "cycles" "" (const_int DEFAULT_INSN_CYCLES))
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- 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.
|
||
+;;-
|
||
+;;- Note: NOTICE_UPDATE_CC in m6809.h handles condition code updates
|
||
+;;- for most instructions.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Test
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;; cmpx is 3 bytes, not 4
|
||
+(define_insn "*tsthi_x"
|
||
+ [(set (cc0) (match_operand:HI 0 "register_operand_x" "v"))]
|
||
+ ""
|
||
+ "cmpx\t#0"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+;; subd #0 is 3 bytes, better than cmpd #0 which is 4 bytes
|
||
+(define_insn "*tsthi_d"
|
||
+ [(set (cc0) (match_operand:HI 0 "register_operand_d" "d"))]
|
||
+ ""
|
||
+ "subd\t#0"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+(define_insn "*tsthi"
|
||
+ [(set (cc0) (match_operand:HI 0 "register_operand" "a"))]
|
||
+ ""
|
||
+ "cmp%0\t#0"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+(define_insn "*bitqi3"
|
||
+ [(set (cc0)
|
||
+ (and:QI (match_operand:QI 0 "register_operand" "%q")
|
||
+ (match_operand:QI 1 "general_operand" "mi")))]
|
||
+ ""
|
||
+ "bit%0\t%1"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+(define_insn "tstqi"
|
||
+ [(set (cc0) (match_operand:QI 0 "nonimmediate_operand" "q,mt"))]
|
||
+ ""
|
||
+ "@
|
||
+ tst%0
|
||
+ tst\t%0"
|
||
+ [(set_attr "length" "1,3")])
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Compare instructions
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;; - cmphi for register to memory or register compares
|
||
+(define_insn "cmphi"
|
||
+ [(set (cc0)
|
||
+ (compare
|
||
+ (match_operand:HI 0 "general_operand" "da, mi, ??Ud")
|
||
+ (match_operand:HI 1 "general_operand" "mi, da, dU")))]
|
||
+ ""
|
||
+{
|
||
+ if ((REG_P (operands[0])) && (REG_P (operands[1]))) {
|
||
+ output_asm_insn ("pshs\t%1\t;cmphi: R:%1 with R:%0", operands);
|
||
+ return "cmp%0\t,s++\t;cmphi:";
|
||
+ }
|
||
+ if (GET_CODE (operands[0]) == REG)
|
||
+ return "cmp%0\t%1\t;cmphi:";
|
||
+ else {
|
||
+ cc_status.flags |= CC_REVERSED;
|
||
+ return "cmp%1\t%0\t;cmphi:(R)";
|
||
+ }
|
||
+}
|
||
+ [(set_attr "length" "5,5,7")])
|
||
+
|
||
+
|
||
+(define_insn "cmpqi"
|
||
+ [(set (cc0)
|
||
+ (compare (match_operand:QI 0 "whole_general_operand" "q,q, q,O,mt,K")
|
||
+ (match_operand:QI 1 "whole_general_operand" "O,mt,K,q,q, q")))]
|
||
+ ""
|
||
+{
|
||
+ if (REG_P (operands[0]) && !M_REG_P (operands[0]))
|
||
+ {
|
||
+ if (operands[1] == const0_rtx)
|
||
+ return "tst%0\t;cmpqi:(ZERO)";
|
||
+ else
|
||
+ return "cmp%0\t%1\t;cmpqi:";
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ cc_status.flags |= CC_REVERSED;
|
||
+
|
||
+ if (operands[0] == const0_rtx)
|
||
+ return "tst%1\t;cmpqi:(RZERO)";
|
||
+ else
|
||
+ return "cmp%1\t%0\t;cmpqi:(R)";
|
||
+ }
|
||
+}
|
||
+ [(set_attr "length" "1,3,2,1,3,2")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Compare/branch pattern
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_expand "cbranchhi4"
|
||
+ [(set (cc0)
|
||
+ (compare
|
||
+ (match_operand:HI 1 "general_operand" "da, mi, ??Ud")
|
||
+ (match_operand:HI 2 "general_operand" "mi, da, dU")))
|
||
+ (set (pc)
|
||
+ (if_then_else
|
||
+ (match_operator 0 "ordered_comparison_operator" [(cc0) (const_int 0)])
|
||
+ (label_ref (match_operand 3 "" ""))
|
||
+ (pc)))]
|
||
+ ""
|
||
+ ""
|
||
+)
|
||
+
|
||
+(define_expand "cbranchqi4"
|
||
+ [(set (cc0)
|
||
+ (compare
|
||
+ (match_operand:QI 1 "whole_general_operand" "q,q, q,O,mt,K")
|
||
+ (match_operand:QI 2 "whole_general_operand" "O,mt,K,q,q, q")))
|
||
+ (set (pc)
|
||
+ (if_then_else
|
||
+ (match_operator 0 "ordered_comparison_operator" [(cc0) (const_int 0)])
|
||
+ (label_ref (match_operand 3 "" ""))
|
||
+ (pc)))]
|
||
+ ""
|
||
+ ""
|
||
+)
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Move
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+; this looks good (obviously not finished) but I still see 'movsi'
|
||
+; places in udivsi3 where it's broken
|
||
+; (define_insn "pushsi1"
|
||
+; [(set (mem:SI (pre_dec (reg:HI HARD_S_REGNUM)))
|
||
+; (match_operand:SI 0 "general_operand" "o"))
|
||
+; (set (reg:HI HARD_S_REGNUM)
|
||
+; (plus:HI (reg:HI HARD_S_REGNUM) (const_int -4))) ]
|
||
+; ""
|
||
+; "; pushsi %0"
|
||
+; [(set_attr "length" "12")])
|
||
+;
|
||
+; (define_insn "popsi1"
|
||
+; [(set (match_operand:SI 0 "general_operand" "=o")
|
||
+; (mem:SI (post_inc (reg:HI HARD_S_REGNUM))))
|
||
+; (set (reg:HI HARD_S_REGNUM)
|
||
+; (plus:HI (reg:HI HARD_S_REGNUM) (const_int 4))) ]
|
||
+; ""
|
||
+; "; popsi %0"
|
||
+; [(set_attr "length" "12")])
|
||
+
|
||
+; (define_insn "movsi"
|
||
+; [(set (match_operand:SI 0 "nonimmediate_operand" "=o")
|
||
+; (match_operand:SI 1 "general_operand" " oi"))]
|
||
+; ""
|
||
+; "; movsi %0 <- %1"
|
||
+; [(set_attr "length" "1")])
|
||
+
|
||
+; this doesn't work
|
||
+; (define_expand "movsi"
|
||
+; [(parallel [
|
||
+; (set (match_operand:SI 0 "nonimmediate_operand" "")
|
||
+; (match_operand:SI 1 "general_operand" ""))
|
||
+; (clobber (match_scratch:HI 2 ""))])]
|
||
+; ""
|
||
+; {
|
||
+; rtx insn;
|
||
+; if (STACK_PUSH_P (operands[0]) || STACK_POP_P (operands[1]))
|
||
+; {
|
||
+; REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
|
||
+; }
|
||
+; insn = emit_move_multi_word (SImode, operands[0], operands[1]);
|
||
+; DONE;
|
||
+; })
|
||
+
|
||
+
|
||
+(define_expand "movhi"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
|
||
+ (match_operand:HI 1 "general_operand" ""))]
|
||
+ ""
|
||
+{
|
||
+ /* One of the ops has to be in a register prior to reload */
|
||
+ if (!register_operand (operand0, HImode) &&
|
||
+ !register_operand (operand1, HImode))
|
||
+ operands[1] = copy_to_mode_reg (HImode, operand1);
|
||
+})
|
||
+
|
||
+;;; Try a splitter to handle failure cases where we try to move
|
||
+;;; an immediate constant (zero usually) directly to memory.
|
||
+;;; This absolutely requires an intermediate register.
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "memory_operand" "")
|
||
+ (match_operand:HI 1 "immediate_operand" ""))
|
||
+ (clobber (match_operand:HI 2 "register_operand" ""))]
|
||
+ ""
|
||
+ [(set (match_dup 2) (match_dup 1))
|
||
+ (set (match_dup 0) (match_dup 2))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; This would be a nice method for loading from a word array,
|
||
+;;; but it is never generated because the combiner cannot merge
|
||
+;;; more than 3 instructions (there are four here). This is
|
||
+;;; perhaps better done via a peephole.
|
||
+(define_insn "*movhi_array_load"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=da")
|
||
+ (mem:HI (plus:HI (ashift:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%B")) (const_int 1))
|
||
+ (match_operand:HI 2 "immediate_operand" "i"))))
|
||
+ (clobber (match_scratch:HI 3 "=X"))]
|
||
+ ""
|
||
+ "ldx\t%2\;abx\;abx\;ld%0\t,x"
|
||
+ [(set_attr "length" "7")])
|
||
+
|
||
+
|
||
+;;; Optimize the move of a byte to the stack using the pshs instruction
|
||
+;;; instead of a store with pre-increment.
|
||
+(define_insn "movhi_push"
|
||
+ [(set (match_operand:HI 0 "push_operand" "=m")
|
||
+ (match_operand:HI 1 "register_operand" "U"))]
|
||
+ ""
|
||
+ "pshs\t%1"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+(define_insn "*movhi_pic_symbolref"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=a")
|
||
+ (match_operand:HI 1 "symbolic_operand" ""))]
|
||
+ "flag_pic"
|
||
+ "lea%0\t%c1,pcr"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_insn "*movhi_1"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=a,d,a,ad,mu")
|
||
+ (match_operand:HI 1 "general_operand" " a,a,d,miu,ad"))]
|
||
+ ""
|
||
+ "@
|
||
+ lea%0\t,%1
|
||
+ tfr\t%1,%0
|
||
+ tfr\t%1,%0
|
||
+ ld%0\t%1
|
||
+ st%1\t%0"
|
||
+ [(set_attr "length" "2,2,2,*,*")])
|
||
+
|
||
+
|
||
+;;; Generated by the combiner to merge an address calculation with
|
||
+;;; a byte load. We can use the 'abx' instruction here.
|
||
+(define_insn "*movqi_array_load"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q")
|
||
+ (mem:QI (plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%B"))
|
||
+ (match_operand:HI 2 "immediate_operand" "i"))))
|
||
+ (clobber (match_scratch:HI 3 "=X"))]
|
||
+ ""
|
||
+ "ldx\t%2\;abx\;ld%0\t,x"
|
||
+ [(set_attr "length" "6")])
|
||
+
|
||
+
|
||
+;;; Optimize the move of a byte to the stack using the pshs instruction
|
||
+;;; instead of a store with pre-increment.
|
||
+(define_insn "movqi_push"
|
||
+ [(set (match_operand:QI 0 "push_operand" "=m")
|
||
+ (match_operand:QI 1 "register_operand" " q"))]
|
||
+ ""
|
||
+ "pshs\t%1"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+;;; Optimize the move of a byte from the stack using the puls instruction
|
||
+;;; instead of a store with post-decrement.
|
||
+(define_insn "movqi_pop"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q")
|
||
+ (match_operand:QI 1 "pop_operand" "m"))]
|
||
+ ""
|
||
+ "puls\t%0"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+;;- load low byte of 16-bit data into 8-bit register/memory
|
||
+(define_insn "*mov_lsb"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,m,!q")
|
||
+ (subreg:QI (match_operand:HI 1 "general_operand" "d,m,a,d, U") 1))]
|
||
+ ""
|
||
+ "@
|
||
+ \t;movlsbqihi: D->B
|
||
+ ld%0\t%L1\t;movlsbqihi: msb:%1 -> R:%0
|
||
+ tfr\t%1,d\t;movlsbqihi: R:%1 -> R:%0
|
||
+ stb\t%0\t;movlsbqihi: R:%1 -> %0
|
||
+ pshs\t%1\t;movlsbqihi: R:%1 -> R:%0\;leas\t1,s\;puls\t%0"
|
||
+ [(set_attr "length" "0,*,2,*,6")])
|
||
+
|
||
+
|
||
+;;- load high byte of 16-bit data into 8-bit register/memory
|
||
+(define_insn "*mov_msb"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,q,m,!q")
|
||
+ (subreg:QI (match_operand:HI 1 "general_operand" "d,O,a,m,d, U") 0))]
|
||
+ ""
|
||
+ "@
|
||
+ tfr\ta,b\t;movmsbqihi: D->B
|
||
+ clr%0\t\t;movmsbqihi: ZERO -> R:%0
|
||
+ tfr\t%1,d\t;movmsbqihi: R:%1 -> R:%0\;tfr\ta,b
|
||
+ ld%0\t%L1\t;movmsbqihi: lsb:%1 -> R:%0
|
||
+ sta\t%0\t;movmsbqihi: R:%1 -> %0
|
||
+ pshs\t%1\t;movmsbqihi: R:%1 -> R:%0\;puls\t%0\;leas\t1,s"
|
||
+ [(set_attr "length" "2,1,4,*,*,6")])
|
||
+
|
||
+
|
||
+(define_insn "*movqi_boolean"
|
||
+ [(set (reg:QI HARD_Z_REGNUM)
|
||
+ (match_operand:QI 0 "general_operand" "q,O,i,m"))]
|
||
+ ""
|
||
+ "@
|
||
+ tst%0
|
||
+ andcc\t#~4
|
||
+ orcc\t#4
|
||
+ tst\t%0")
|
||
+
|
||
+
|
||
+(define_insn "movqi"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,m,q,m,q,z")
|
||
+ (match_operand:QI 1 "general_operand" " q,O,O,mi,q,z,q"))]
|
||
+ ""
|
||
+ "@
|
||
+ tfr\t%1,%0
|
||
+ clr%0
|
||
+ clr\t%0
|
||
+ ld%0\t%1
|
||
+ st%1\t%0
|
||
+ tfr\tcc,%0\;and%0\t#4
|
||
+ tst%0"
|
||
+ [(set_attr "length" "2,1,3,*,*,4,1")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Swap registers
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+; Note: 8-bit swap is never needed so it is not defined.
|
||
+
|
||
+(define_insn "swaphi"
|
||
+ [(set (match_operand:HI 0 "register_operand" "+r")
|
||
+ (match_operand:HI 1 "register_operand" "+r"))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ ""
|
||
+ "exg\t%1,%0"
|
||
+ [(set_attr "length" "2")
|
||
+ (set (attr "cycles") (const_int EXG_CYCLES))])
|
||
+
|
||
+
|
||
+(define_insn "bswaphi2"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (bswap:HI (match_operand:HI 1 "register_operand" "0")))]
|
||
+ ""
|
||
+ "exg\ta,b"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Extension and truncation insns.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "extendqihi2"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (sign_extend:HI (match_operand:QI 1 "general_operand" "B")))]
|
||
+ ""
|
||
+ "sex\t\t;extendqihi2: R:%1 -> R:%0"
|
||
+ [(set_attr "length" "1")])
|
||
+
|
||
+
|
||
+(define_insn "zero_extendqihi2"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (zero_extend:HI (match_operand:QI 1 "general_operand" "B")))]
|
||
+ ""
|
||
+ "clra\t\t;zero_extendqihi: R:%1 -> R:%0"
|
||
+ [(set_attr "length" "1")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- All kinds of add instructions.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+
|
||
+;;
|
||
+;; gcc's automatic version of addsi3 doesn't know about adcb,adca
|
||
+;; so it is MUCH less efficient. Define this one ourselves.
|
||
+;;
|
||
+;; TODO - can't always get 'd' for the clobber... allow other registers
|
||
+;; as well and use exg d,R ... exg R,d around the code sequence to
|
||
+;; use others, at a price. Also consider libcall for this when
|
||
+;; optimizing for size.
|
||
+;;
|
||
+(define_insn "addsi3"
|
||
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=o")
|
||
+ (plus:SI (match_operand:SI 1 "general_operand" "%o")
|
||
+ (match_operand:SI 2 "general_operand" " oi")))
|
||
+ (clobber (match_scratch:HI 3 "=d"))]
|
||
+ ""
|
||
+{
|
||
+ m6809_output_addsi3 (PLUS, operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "21")])
|
||
+
|
||
+
|
||
+; Increment of a 16-bit MEM by 1 can be done without a register.
|
||
+(define_insn "*addhi_mem_1"
|
||
+ [(set (match_operand:HI 0 "memory_operand" "=m")
|
||
+ (plus:HI (match_dup 0) (const_int 1)))]
|
||
+ "GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF"
|
||
+{
|
||
+ rtx xoperands[2];
|
||
+
|
||
+ xoperands[0] = operands[0];
|
||
+ xoperands[1] = adjust_address (operands[0], QImode, 1);
|
||
+
|
||
+ output_asm_insn ("inc\t%1", xoperands);
|
||
+ output_asm_insn ("bne\t__IL%=", xoperands);
|
||
+ output_asm_insn ("inc\t%0\;__IL%=:", xoperands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "7")])
|
||
+
|
||
+
|
||
+; Decrement of a 16-bit MEM by 1 can be done without a register.
|
||
+(define_insn "*addhi_mem_minus1"
|
||
+ [(set (match_operand:HI 0 "memory_operand" "=m")
|
||
+ (plus:HI (match_dup 0) (const_int -1)))]
|
||
+ "GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF"
|
||
+{
|
||
+ rtx xoperands[2];
|
||
+
|
||
+ xoperands[0] = operands[0];
|
||
+ xoperands[1] = adjust_address (operands[0], QImode, 1);
|
||
+
|
||
+ output_asm_insn ("tst\t%1", xoperands);
|
||
+ output_asm_insn ("bne\t__IL%=", xoperands);
|
||
+ output_asm_insn ("dec\t%0", xoperands);
|
||
+ output_asm_insn ("__IL%=:", xoperands);
|
||
+ output_asm_insn ("dec\t%1", xoperands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "7")])
|
||
+
|
||
+
|
||
+; Allow the addition of an 8-bit quantity to a 16-bit quantity
|
||
+; using the LEAX B,Y addressing mode, where X and Y are both
|
||
+; index registers. This will only get generated via the peephole
|
||
+; which removes a sign extension.
|
||
+(define_insn "*addhi_b"
|
||
+ [(set (match_operand:HI 0 "index_register_operand" "=a")
|
||
+ (plus:HI(match_operand:HI 1 "index_register_operand" "%a")
|
||
+ (match_operand:QI 2 "register_operand" "q")
|
||
+ ))]
|
||
+ ""
|
||
+ "lea%0\t%2,%1\t;addhi_b: R:%0 = R:%2 + R:%1"
|
||
+ [(set_attr "length" "*")])
|
||
+
|
||
+
|
||
+; Splitter for addhi pattern #5 below
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "index_register_operand" "")
|
||
+ (plus:HI (match_dup 0) (match_operand:HI 1 "memory_operand" "")))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (parallel [(set (match_dup 0) (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (match_dup 0))])
|
||
+ (set (reg:HI HARD_D_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_D_REGNUM) (match_dup 1)))
|
||
+ (parallel [(set (match_dup 0) (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (match_dup 0))])
|
||
+ ]
|
||
+{
|
||
+})
|
||
+
|
||
+
|
||
+; Splitter for addhi pattern #7 below
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "index_register_operand" "")
|
||
+ (plus:HI (match_dup 0) (match_operand:HI 1 "index_register_operand" "")))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (parallel [(set (match_dup 1) (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (match_dup 1))])
|
||
+ (set (match_dup 0)
|
||
+ (plus:HI (reg:HI HARD_D_REGNUM) (match_dup 0)))
|
||
+ (parallel [(set (match_dup 1) (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (match_dup 1))])
|
||
+ ]
|
||
+{
|
||
+})
|
||
+
|
||
+
|
||
+; TODO - this is ugly. During RTL generation, we don't know what registers
|
||
+; are available, so the multiple-insn sequences can only be solved
|
||
+; via 'define_split's during matching. See andhi3 for an example.
|
||
+; Keep the constraints with ? modifiers to help reload pick the right
|
||
+; registers.
|
||
+;
|
||
+; The forms are:
|
||
+; 1. D += D, expand this into a shift instead. (rtx costs should be corrected
|
||
+; to avoid this even happening...)
|
||
+; 2. D += U, require U to be pushed to memory. (Lots of patterns do this
|
||
+; now, is this a better way?)
|
||
+; 3. Best choice: 'addd'
|
||
+; 4. Next best choice: 'lea'
|
||
+; 5. Hybrid of 3 and 4
|
||
+; 6. Same as 4, not bad
|
||
+; 7. BAD, no D register at all
|
||
+; 8. 'lea', as good as 4.
|
||
+(define_insn "addhi3"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d, d, d, a,?a, a,???T,a")
|
||
+ (plus:HI(match_operand:HI 1 "add_general_operand" "%0, 0, 0, d, 0, a, 0, a")
|
||
+ (match_operand:HI 2 "general_operand" " 0, !U, mi, a, m, d, T, i")
|
||
+ ))]
|
||
+ ""
|
||
+ "@
|
||
+ lslb\t\t;addhi: R:%0 += R:%2\;rola\t\t;also R:%0 *= 2
|
||
+ pshs\t%2\t;addhi: R:%0 += R:%2\;add%0\t,s++
|
||
+ add%0\t%2
|
||
+ lea%0\t%1,%2
|
||
+ #
|
||
+ lea%0\t%2,%1
|
||
+ #
|
||
+ lea%0\t%a2,%1"
|
||
+ [(set_attr "length" "2,6,*,*,7,*,7,*")])
|
||
+
|
||
+
|
||
+(define_insn "addqi3_carry"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q")
|
||
+ (unspec:QI [
|
||
+ (match_operand:QI 1 "whole_general_operand" "%0")
|
||
+ (match_operand:QI 2 "whole_general_operand" "tmi")] UNSPEC_ADD_CARRY))]
|
||
+ ""
|
||
+ "adc%0\t%2\t;addqi_carry: R:%0 += %2"
|
||
+ [(set_attr "length" "*")])
|
||
+
|
||
+
|
||
+; TODO: specifying 'A' for the first constraint, to force into the A register
|
||
+; is not working because of the way registers are currently set up. This will
|
||
+; take some work to get right. Thus the second alternative as a backup.
|
||
+(define_insn "addqi3_decimal"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=A,?q")
|
||
+ (unspec:QI [
|
||
+ (match_operand:QI 1 "general_operand" "%0,0")
|
||
+ (match_operand:QI 2 "general_operand" "mi,mi")] UNSPEC_ADD_DECIMAL))]
|
||
+ ""
|
||
+ "@
|
||
+ adda\t%2\;daa
|
||
+ tfr\t%0,a\;adda\t%2\;daa\;tfr\ta,%0"
|
||
+ [(set_attr "length" "5,9")])
|
||
+
|
||
+
|
||
+(define_insn "addqi3"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,m,m,q")
|
||
+ (plus:QI (match_operand:QI 1 "whole_general_operand" "%0,0,0,0,0,0")
|
||
+ (match_operand:QI 2 "whole_general_operand" " 0,I,N,I,N,mi")))]
|
||
+ ""
|
||
+ "@
|
||
+ asl%0\t\t;addqi: R:%0 = R:%0 + R:%0
|
||
+ inc%0
|
||
+ dec%0
|
||
+ inc\t%0
|
||
+ dec\t%0
|
||
+ add%0\t%2"
|
||
+ [(set_attr "length" "1,1,1,3,3,*")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Subtract instructions.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "subsi3"
|
||
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=o")
|
||
+ (minus:SI (match_operand:SI 1 "general_operand" " o")
|
||
+ (match_operand:SI 2 "general_operand" " oi")))
|
||
+ (clobber (match_scratch:HI 3 "=d"))]
|
||
+ ""
|
||
+{
|
||
+ m6809_output_addsi3 (MINUS, operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "21")])
|
||
+
|
||
+
|
||
+(define_insn "subhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d, d, a")
|
||
+ (minus:HI (match_operand:HI 1 "register_operand" "0, 0, 0")
|
||
+ (match_operand:HI 2 "general_operand" "mi, ?U,n")))]
|
||
+ ""
|
||
+ "@
|
||
+ sub%0\t%2\t;subhi: R:%0 -= %2
|
||
+ pshs\t%2\t;subhi: R:%0 -= R:%2\;sub%0\t,s++
|
||
+ lea%0\t%n2,%1\t;subhi: R:%0 = R:%1 + %n2"
|
||
+ [(set_attr "length" "*,5,3")])
|
||
+
|
||
+
|
||
+(define_insn "subqi3_carry"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q")
|
||
+ (unspec:QI [
|
||
+ (match_operand:QI 1 "whole_general_operand" "%0")
|
||
+ (match_operand:QI 2 "whole_general_operand" "tmi")] UNSPEC_SUB_CARRY))]
|
||
+ ""
|
||
+ "sbc%0\t%2\t;subqi_carry: R:%0 += %2"
|
||
+ [(set_attr "length" "*")])
|
||
+
|
||
+
|
||
+(define_insn "subqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q, q, !q, q")
|
||
+ (minus:QI (match_operand:QI 1 "whole_register_operand" "0, 0, I, 0")
|
||
+ (match_operand:QI 2 "whole_general_operand" "I, mi, 0, t")))]
|
||
+ ""
|
||
+ "@
|
||
+ dec%0
|
||
+ sub%0\t%2
|
||
+ dec%0\;neg%0
|
||
+ sub%0\t%2"
|
||
+ [(set_attr "length" "1,3,2,3")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Multiply instructions.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+; TODO - merge these two instructions, using 'extend_operator' to match
|
||
+; either signed or zero extension. Everything else is the same.
|
||
+(define_insn "mulqihi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (mult:HI (sign_extend:HI (match_operand:QI 1 "general_operand" "%q"))
|
||
+ (match_operand:QI 2 "general_operand" "tmK")))]
|
||
+ ""
|
||
+ "lda\t%2\t;mulqihi3\;mul"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+(define_insn "umulqihi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (mult:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "%q"))
|
||
+ (match_operand:QI 2 "general_operand" "tmK")))]
|
||
+ ""
|
||
+ "lda\t%2\t;umulqihi3\;mul"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+; Expand a 16x16 multiplication into either a libcall or a shift.
|
||
+; If the second operand is a small constant, use the above form.
|
||
+; Otherwise, do a libcall.
|
||
+(define_expand "mulhi3"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
|
||
+ (mult:HI (match_operand:HI 1 "general_operand" "")
|
||
+ (match_operand:HI 2 "nonmemory_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (HImode, "mulhi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Divide instructions.
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_expand "divhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (div:HI (match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (HImode, "divhi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "divqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (div:QI (match_operand:QI 1 "register_operand" "")
|
||
+ (match_operand:QI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (QImode, "divqi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "udivhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (udiv:HI (match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (HImode, "udivhi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- mod
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_expand "modhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (mod:HI (match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (HImode, "modhi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "modqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (mod:QI (match_operand:QI 1 "register_operand" "")
|
||
+ (match_operand:QI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (QImode, "modqi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "umodhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (umod:HI (match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "register_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+ emit_libcall_insns (HImode, "umodhi3", operands, 2);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- and, or, xor common patterns
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+; Split a bitwise HImode into two QImode instructions, with one of
|
||
+; the sources in a pushable register. The register is pushed onto
|
||
+; the stack and memory pop operands (,s+) are used in the QI forms.
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (match_operator:HI 3 "logical_bit_operator"
|
||
+ [(match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "register_operand" "")]))]
|
||
+ "reload_completed"
|
||
+ [(set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 2))
|
||
+ (set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_A_REGNUM)
|
||
+ (mem:QI (post_inc:QI (reg:HI HARD_S_REGNUM)))]))
|
||
+ (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_D_REGNUM)
|
||
+ (mem:QI (post_inc:QI (reg:HI HARD_S_REGNUM)))]))
|
||
+ (use (reg:QI HARD_A_REGNUM))]
|
||
+{
|
||
+})
|
||
+
|
||
+; Split a bitwise HImode into two QImode instructions, with one
|
||
+; of the sources being a (MEM (MEM (...)); i.e. an indirect memory
|
||
+; reference. This requires dereferencing the pointer into a
|
||
+; temporary register (X), which must be saved/restored around the
|
||
+; compute instructions.
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (match_operator:HI 3 "logical_bit_operator"
|
||
+ [(match_operand:HI 1 "register_operand" "")
|
||
+ (mem:HI (match_operand:HI 2 "memory_operand" ""))]))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 4))
|
||
+ (set (match_dup 4) (match_dup 2))
|
||
+ (set (match_dup 4) (mem:HI (match_dup 4)))
|
||
+ (set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_A_REGNUM)
|
||
+ (mem:QI (post_inc:QI (match_dup 4)))]))
|
||
+ (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_D_REGNUM)
|
||
+ (mem:QI (post_inc:QI (match_dup 4)))]))
|
||
+ (use (reg:QI HARD_A_REGNUM))
|
||
+ (set (match_dup 4) (mem:HI (post_inc:HI (reg:HI HARD_S_REGNUM))))
|
||
+ ]
|
||
+{
|
||
+ /* Use X for a temporary index register */
|
||
+ operands[4] = gen_rtx_REG (HImode, HARD_X_REGNUM);
|
||
+})
|
||
+
|
||
+
|
||
+; Split a bitwise HImode into two QImode instructions. This is
|
||
+; the common case. This handles splitting when neither of the
|
||
+; above two cases applies.
|
||
+(define_split
|
||
+ [(set (match_operand:HI 0 "register_operand" "")
|
||
+ (match_operator:HI 3 "logical_bit_operator"
|
||
+ [(match_operand:HI 1 "register_operand" "")
|
||
+ (match_operand:HI 2 "general_operand" "")]))]
|
||
+ "reload_completed"
|
||
+ [(set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_A_REGNUM) (match_dup 4)]))
|
||
+ (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3
|
||
+ [(reg:QI HARD_D_REGNUM) (match_dup 5)]))
|
||
+ (use (reg:QI HARD_A_REGNUM))]
|
||
+{
|
||
+ if (GET_CODE (operands[2]) == CONST_INT)
|
||
+ {
|
||
+ operands[4] = gen_rtx_const_high (operands[2]);
|
||
+ operands[5] = gen_rtx_const_low (operands[2]);
|
||
+ }
|
||
+ else if ((GET_CODE (operands[2]) == MEM)
|
||
+ && (GET_CODE (XEXP (operands[2], 0)) == MEM))
|
||
+ {
|
||
+ FAIL;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ operands[4] = gen_highpart (QImode, operands[2]);
|
||
+ operands[5] = gen_lowpart (QImode, operands[2]);
|
||
+ }
|
||
+})
|
||
+
|
||
+; Below are the specific cases for each of the operators.
|
||
+; The QImode versions are the simplest and can be implemented
|
||
+; directly on the hardware. The HImode cases are all output
|
||
+; using one of the above splitting techniques.
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- and
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "andhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (and:HI (match_operand:HI 1 "register_operand" "%0")
|
||
+ (match_operand:HI 2 "general_operand" "mnU")))]
|
||
+ ""
|
||
+ "#")
|
||
+
|
||
+;; it is not clear that this is correct
|
||
+(define_insn "*andqi_2"
|
||
+ [(set
|
||
+ (match_operand:QI 0 "register_operand" "=q")
|
||
+ (and:QI (match_operand:QI 1 "register_operand" "q")
|
||
+ (match_operand 2 "const_int_operand" "i")))]
|
||
+ ""
|
||
+{
|
||
+ if (GET_CODE (operands[2]) == CONST_INT)
|
||
+ {
|
||
+ operands[3] = GEN_INT(INTVAL(operands[2]) & 0xff);
|
||
+ return "and%0 %3";
|
||
+ }
|
||
+
|
||
+ return "and%0 %2";
|
||
+}
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+(define_insn "andqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q,q,q,qc")
|
||
+ (and:QI (match_operand:QI 1 "whole_register_operand" "%0,0,0,0")
|
||
+ (match_operand:QI 2 "whole_general_operand" " O,N,m,i")))]
|
||
+ ""
|
||
+ "@
|
||
+ clr%0\t;andqi(ZERO)
|
||
+ \t;andqi(-1)
|
||
+ and%0\t%2
|
||
+ and%0\t%2"
|
||
+ [(set_attr "length" "1,0,3,2")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- or
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "iorhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (ior:HI (match_operand:HI 1 "register_operand" "%0")
|
||
+ (match_operand:HI 2 "general_operand" "mnU")))]
|
||
+ ""
|
||
+ "#")
|
||
+
|
||
+
|
||
+(define_insn "iorqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q,q, qc")
|
||
+ (ior:QI (match_operand:QI 1 "whole_register_operand" "%0,0, 0")
|
||
+ (match_operand:QI 2 "whole_general_operand" " O,m,i")))]
|
||
+ ""
|
||
+ "@
|
||
+ \t;iorqi(ZERO)
|
||
+ or%0\t%2
|
||
+ or%0\t%2"
|
||
+ [(set_attr "length" "0,3,2")])
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- xor
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "xorhi3"
|
||
+ [(set (match_operand:HI 0 "register_operand" "=d")
|
||
+ (xor:HI (match_operand:HI 1 "register_operand" "%0")
|
||
+ (match_operand:HI 2 "general_operand" "mnU")))]
|
||
+ ""
|
||
+ "#")
|
||
+
|
||
+
|
||
+(define_insn "xorqi3"
|
||
+ [(set (match_operand:QI 0 "register_operand" "=q,q,q,q")
|
||
+ (xor:QI (match_operand:QI 1 "whole_register_operand" "%0,0,0,0")
|
||
+ (match_operand:QI 2 "whole_general_operand" " O,N,m,i")))]
|
||
+ ""
|
||
+ "@
|
||
+ \t;xorqi(ZERO)
|
||
+ com%0\t;xorqi(-1)
|
||
+ eor%0\t%2
|
||
+ eor%0\t%2"
|
||
+ [(set_attr "length" "0,1,3,2")])
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Two's Complements
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "neghi2"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,!a")
|
||
+ (neg:HI (match_operand:HI 1 "general_operand" "0, 0")))]
|
||
+ ""
|
||
+ "@
|
||
+ nega\;negb\;sbca\t#0
|
||
+ exg\td,%0\;nega\;negb\;sbca\t#0\;exg\td,%0"
|
||
+ [(set_attr "length" "5,9")])
|
||
+
|
||
+
|
||
+(define_insn "negqi2"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m")
|
||
+ (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))]
|
||
+ ""
|
||
+ "@
|
||
+ neg%0
|
||
+ neg\t%0"
|
||
+ [(set_attr "length" "1,3")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- One's Complements
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_insn "one_cmplhi2"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,?tm,???a")
|
||
+ (not:HI (match_operand:HI 1 "general_operand" "0, 0, 0")))]
|
||
+ ""
|
||
+ "@
|
||
+ coma\;comb
|
||
+ com\t%0\;com\t%L0
|
||
+ exg\td,%0\;coma\;comb\;exg\td,%0"
|
||
+ [(set_attr "length" "2,6,6")])
|
||
+
|
||
+
|
||
+(define_insn "one_cmplqi2"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m")
|
||
+ (not:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))]
|
||
+ ""
|
||
+ "@
|
||
+ com%0
|
||
+ com\t%0"
|
||
+ [(set_attr "length" "1,3")])
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Shifts/rotates
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+(define_code_iterator bit_code [ashift ashiftrt lshiftrt])
|
||
+(define_code_attr bit_code_name [(ashift "ashl") (ashiftrt "ashr") (lshiftrt "lshr")])
|
||
+
|
||
+(define_mode_iterator bit_mode [QI HI])
|
||
+(define_mode_attr bit_mode_name [(QI "qi3") (HI "hi3")])
|
||
+
|
||
+;; Emit RTL for any shift (handles all 3 opcodes and 2 mode sizes)
|
||
+
|
||
+(define_expand "<bit_code:bit_code_name><bit_mode:bit_mode_name>"
|
||
+ [(set (match_operand:bit_mode 0 "nonimmediate_operand" "")
|
||
+ (bit_code:bit_mode (match_operand:bit_mode 1 "general_operand" "")
|
||
+ (match_operand:bit_mode 2 "nonmemory_operand" "")))]
|
||
+ ""
|
||
+{
|
||
+})
|
||
+
|
||
+; Individual instructions implemented in the CPU.
|
||
+
|
||
+
|
||
+(define_insn "*ashift1"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q")
|
||
+ (ashift:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))]
|
||
+ ""
|
||
+ "@
|
||
+ asl\t%0
|
||
+ asl%0"
|
||
+ [(set_attr "length" "3,1")])
|
||
+
|
||
+(define_insn "*lshiftrt1"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q")
|
||
+ (lshiftrt:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))]
|
||
+ ""
|
||
+ "@
|
||
+ lsr\t%0
|
||
+ lsr%0"
|
||
+ [(set_attr "length" "3,1")])
|
||
+
|
||
+(define_insn "*ashiftrt1"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q")
|
||
+ (ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))]
|
||
+ ""
|
||
+ "@
|
||
+ asr\t%0
|
||
+ asr%0"
|
||
+ [(set_attr "length" "3,1")])
|
||
+
|
||
+(define_insn "*rotate1"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q")
|
||
+ (rotate:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))]
|
||
+ ""
|
||
+ "@
|
||
+ rol\t%0
|
||
+ rol%0"
|
||
+ [(set_attr "length" "3,1")])
|
||
+
|
||
+
|
||
+(define_insn "*rotatert1"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q")
|
||
+ (rotatert:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))]
|
||
+ ""
|
||
+ "@
|
||
+ ror\t%0
|
||
+ ror%0"
|
||
+ [(set_attr "length" "3,1")])
|
||
+
|
||
+
|
||
+; A shift by 8 for D reg can be optimized by just moving
|
||
+; between the A/B halves, and then zero/sign extending or
|
||
+; filling in zeroes.
|
||
+; Because GCC does not understand that 'A' and 'D' refer to
|
||
+; the same storage location, we must use 'USE' throughout
|
||
+; to prevent deletion of 'unnecessary' instructions.
|
||
+; Similar optimization for MEM would require a scratch register
|
||
+; so is not done here.
|
||
+
|
||
+(define_split
|
||
+ [(set (reg:HI HARD_D_REGNUM) (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 8)))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (use (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:QI HARD_A_REGNUM) (reg:QI HARD_D_REGNUM))
|
||
+ (use (reg:QI HARD_A_REGNUM))
|
||
+ (set (reg:QI HARD_D_REGNUM) (const_int 0))
|
||
+ ]
|
||
+ "")
|
||
+
|
||
+(define_split
|
||
+ [(set (reg:HI HARD_D_REGNUM) (lshiftrt:HI (reg:HI HARD_D_REGNUM) (const_int 8)))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (use (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:QI HARD_D_REGNUM) (reg:QI HARD_A_REGNUM))
|
||
+ (use (reg:QI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (zero_extend:HI (reg:QI HARD_D_REGNUM)))
|
||
+ ]
|
||
+ "")
|
||
+
|
||
+(define_split
|
||
+ [(set (reg:HI HARD_D_REGNUM) (ashiftrt:HI (reg:HI HARD_D_REGNUM) (const_int 8)))]
|
||
+ "reload_completed"
|
||
+ [
|
||
+ (use (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:QI HARD_D_REGNUM) (reg:QI HARD_A_REGNUM))
|
||
+ (use (reg:QI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM)))
|
||
+ ]
|
||
+ "")
|
||
+
|
||
+
|
||
+; On the WPC hardware, there is a shift register that can be used
|
||
+; to compute (1<<n) efficiently in two instructions. Note that this
|
||
+; form only works when using -mint8 though, because C will promote
|
||
+; to 'int' when doing this operation. TODO : we need a 16-bit form too.
|
||
+(define_insn "ashlqi3_wpc"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q")
|
||
+ (ashift:QI (match_operand:QI 1 "immediate_operand" "I")
|
||
+ (match_operand:QI 2 "general_operand" "q")))]
|
||
+ "TARGET_WPC"
|
||
+ "st%2\t0x3FF7\;ld%0\t0x3FF7"
|
||
+ [(set_attr "length" "6")])
|
||
+
|
||
+
|
||
+; Internal instructions for shifting by a constant.
|
||
+; Two forms are provided, one for QImode, one for HImode.
|
||
+; These are always split into the above instructions
|
||
+; (except for QImode forms that directly match one of the
|
||
+; above instructions, in which the condition will not
|
||
+; allow the splitter to match).
|
||
+
|
||
+(define_insn_and_split "<bit_code:bit_code_name>hi3_const"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=dm")
|
||
+ (bit_code:HI (match_operand:HI 1 "general_operand" "0")
|
||
+ (match_operand:HI 2 "immediate_operand" "n")))]
|
||
+ ""
|
||
+ "#"
|
||
+ "reload_completed"
|
||
+ [(const_int 0)]
|
||
+{
|
||
+ m6809_split_shift (<bit_code:CODE>, operands);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_insn_and_split "<bit_code:bit_code_name>qi3_const"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
|
||
+ (bit_code:QI (match_operand:QI 1 "general_operand" "0")
|
||
+ (match_operand:QI 2 "immediate_operand" "n")))]
|
||
+ "INTVAL (operands[2]) > 1"
|
||
+ "#"
|
||
+ "&& reload_completed"
|
||
+ [(const_int 0)]
|
||
+{
|
||
+ m6809_split_shift (<bit_code:CODE>, operands);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+; Internal instructions for shifting by a nonconstant.
|
||
+; These expand into complex assembly.
|
||
+
|
||
+(define_insn "<bit_code:bit_code_name>hi3_reg"
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d")
|
||
+ (bit_code:HI (match_operand:HI 1 "general_operand" "0")
|
||
+ (match_operand:HI 2 "nonimmediate_operand" "v")))]
|
||
+ ""
|
||
+{
|
||
+ m6809_output_shift_insn (<bit_code:CODE>, operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "20")])
|
||
+
|
||
+
|
||
+(define_insn "<bit_code:bit_code_name>qi3_reg"
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=q")
|
||
+ (bit_code:QI (match_operand:QI 1 "general_operand" "0")
|
||
+ (match_operand:QI 2 "nonimmediate_operand" "v")))]
|
||
+ ""
|
||
+{
|
||
+ m6809_output_shift_insn (<bit_code:CODE>, operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "16")])
|
||
+
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Jumps and transfers
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;;; The casesi pattern is normally *not* defined; see 'tablejump' instead.
|
||
+(define_expand "casesi"
|
||
+ [(match_operand:HI 0 "register_operand" "") ; index to jump on
|
||
+ (match_operand:HI 1 "immediate_operand" "") ; lower bound
|
||
+ (match_operand:HI 2 "immediate_operand" "") ; total range
|
||
+ (match_operand 3 "" "") ; table label
|
||
+ (match_operand 4 "" "")] ; out of range label
|
||
+ "TARGET_BYTE_INT && TARGET_CASESI"
|
||
+{
|
||
+ m6809_do_casesi (operands[0], operands[1], operands[2],
|
||
+ operands[3], operands[4]);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+(define_insn "tablejump_short_offset"
|
||
+ [(set (pc)
|
||
+ (mem:HI (plus:HI (match_operand:HI 1 "register_operand" "U")
|
||
+ (zero_extend:HI (match_operand:QI 0 "register_operand" "q")))))]
|
||
+ ""
|
||
+ "jmp\t[b,x]\t;tablejump_short_offset"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+(define_insn "tablejump_long_offset"
|
||
+ [(set (pc)
|
||
+ (mem:HI (plus:HI (match_operand:HI 1 "register_operand" "U")
|
||
+ (match_operand:HI 0 "register_operand" "d"))))]
|
||
+ ""
|
||
+ "jmp\t[d,x]\t;tablejump_long_offset"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+ ;; A tablejump operation gives the address in operand 0, with the
|
||
+ ;; CODE_LABEL for the table in operand 1. The 'define_expand'
|
||
+ ;; shows the arguments as GCC presents them. For a register
|
||
+ ;; operand, the assembly code is straightforward. For a MEM,
|
||
+ ;; assumed to be a SYMBOL_REF, two forms are given, one normal
|
||
+ ;; and one for PIC mode.
|
||
+ (define_expand "tablejump"
|
||
+ [(parallel [
|
||
+ (set (pc) (match_operand:HI 0 "" ""))
|
||
+ (use (label_ref (match_operand 1 "" "")))
|
||
+ (clobber (match_scratch:HI 2 ""))
|
||
+ ])]
|
||
+ ""
|
||
+ {
|
||
+ })
|
||
+
|
||
+
|
||
+(define_insn "*tablejump_reg"
|
||
+ [(parallel [
|
||
+ (set (pc)
|
||
+ (match_operand:HI 0 "register_operand" "a"))
|
||
+ (use (label_ref (match_operand 1 "" "")))
|
||
+ (clobber (match_scratch:HI 2 ""))
|
||
+ ])]
|
||
+ ""
|
||
+ "jmp\t,%0"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+(define_insn "*tablejump_symbol"
|
||
+ [(parallel [
|
||
+ (set (pc)
|
||
+ (mem:HI
|
||
+ (plus:HI (match_operand:HI 0 "register_operand" "a")
|
||
+ (label_ref (match_operand 1 "" "")))))
|
||
+ (use (label_ref (match_dup 1)))
|
||
+ (clobber (match_scratch:HI 2 ""))
|
||
+ ])]
|
||
+ "!flag_pic"
|
||
+{
|
||
+ output_asm_insn ("jmp\t[%a1,%0]", operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_insn "*tablejump_symbol_pic"
|
||
+ [(parallel [
|
||
+ (set (pc)
|
||
+ (mem:HI
|
||
+ (plus:HI (match_operand:HI 0 "register_operand" "d")
|
||
+ (label_ref (match_operand 1 "" "")))))
|
||
+ (use (label_ref (match_dup 1)))
|
||
+ (clobber (match_scratch:HI 2 "=&a"))
|
||
+ ])]
|
||
+ "flag_pic"
|
||
+{
|
||
+ output_asm_insn ("lea%2\t%a1,pcr", operands);
|
||
+ output_asm_insn ("ld%0\t%0,%2", operands);
|
||
+ output_asm_insn ("jmp\t%0,%2", operands);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "8")])
|
||
+
|
||
+
|
||
+(define_insn "indirect_jump"
|
||
+ [(set (pc)
|
||
+ (match_operand:HI 0 "register_operand" "a"))]
|
||
+ ""
|
||
+ "jmp\t,%0"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+(define_insn "jump"
|
||
+ [(set (pc) (label_ref (match_operand 0 "" "")))]
|
||
+ ""
|
||
+{
|
||
+ return output_branch_insn ( LABEL_REF, operands, get_attr_length (insn));
|
||
+}
|
||
+ [(set (attr "type") (const_string "branch"))])
|
||
+
|
||
+; Output assembly for a condition branch instruction.
|
||
+(define_insn "*cond_branch"
|
||
+ [(set (pc)
|
||
+ (if_then_else
|
||
+ (match_operator 1 "comparison_operator" [(cc0) (const_int 0)])
|
||
+ (label_ref (match_operand 0 "" "")) (pc)))]
|
||
+ ""
|
||
+{
|
||
+ return output_branch_insn ( GET_CODE(operands[1]),
|
||
+ operands, get_attr_length (insn));
|
||
+}
|
||
+ [(set (attr "type") (const_string "cbranch"))])
|
||
+
|
||
+
|
||
+; Similar to above, but for a condition branch instruction that
|
||
+; had its operands reversed at some point.
|
||
+(define_insn "*cond_branch_reverse"
|
||
+ [(set (pc)
|
||
+ (if_then_else
|
||
+ (match_operator 1 "comparison_operator" [(cc0) (const_int 0)])
|
||
+ (pc) (label_ref (match_operand 0 "" ""))))]
|
||
+ ""
|
||
+{
|
||
+ return output_branch_insn ( reverse_condition (GET_CODE(operands[1])),
|
||
+ operands, get_attr_length (insn));
|
||
+}
|
||
+ [(set (attr "type") (const_string "cbranch"))])
|
||
+
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Calls
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;; Generate a call instruction for a function that does not
|
||
+;; return a value. The expander is used during RTL generation.
|
||
+;; The instructions below are used during matching; only one
|
||
+;; of them will be used, depending on the type of function
|
||
+;; being called. The different conditions are:
|
||
+;;
|
||
+;; 1) far_functionp - is this a far function? Those need
|
||
+;; to be output as indirect calls through a far-function
|
||
+;; handler.
|
||
+;;
|
||
+;; 2) noreturn_functionp - if the function does not return,
|
||
+;; we can use a 'jmp' instead of a 'jsr' to call it.
|
||
+;;
|
||
+;; 3) is PIC mode enabled? If so, we'll always use
|
||
+;; relative calls (lbsr or lbra).
|
||
+;;
|
||
+;; Note: not all combinations are fully supported, especially
|
||
+;; relating to PIC.
|
||
+;;
|
||
+;; The 'bsr' instruction is never generated.
|
||
+
|
||
+(define_expand "call"
|
||
+ [(call (match_operand:HI 0 "memory_operand" "")
|
||
+ (match_operand:HI 1 "general_operand" ""))]
|
||
+ ""
|
||
+ "")
|
||
+
|
||
+(define_insn "*call_nopic_far"
|
||
+ [(call (match_operand:HI 0 "memory_operand" "m")
|
||
+ (match_operand:HI 1 "general_operand" "g"))]
|
||
+ "far_functionp (operands[0])"
|
||
+{
|
||
+ output_far_call_insn (operands, 0);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "6")])
|
||
+
|
||
+
|
||
+; PIC forms come first, and should only match
|
||
+; (MEM (SYMBOL_REF)). Other MEM forms are treated as usual.
|
||
+(define_insn "*call_pic"
|
||
+ [(call (mem:HI (match_operand:HI 0 "symbolic_operand" ""))
|
||
+ (match_operand:HI 1 "general_operand" "g"))]
|
||
+ "flag_pic && !noreturn_functionp (operands[0])"
|
||
+ "lbsr\t%C0"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_insn "*call_nopic"
|
||
+ [(call (match_operand:HI 0 "memory_operand" "m")
|
||
+ (match_operand:HI 1 "general_operand" "g"))]
|
||
+ "!noreturn_functionp (operands[0])"
|
||
+ "jsr\t%0"
|
||
+ [(set_attr "length" "3")
|
||
+ (set (attr "cycles") (const_int JSR_EXTENDED_CYCLES))])
|
||
+
|
||
+
|
||
+(define_insn "*call_noreturn_pic"
|
||
+ [(call (mem:HI (match_operand:HI 0 "symbolic_operand" ""))
|
||
+ (match_operand:HI 1 "general_operand" "g"))]
|
||
+ "flag_pic && noreturn_functionp (operands[0])"
|
||
+ "lbra\t%C0"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_insn "*call_noreturn_nopic"
|
||
+ [(call (match_operand:HI 0 "memory_operand" "m")
|
||
+ (match_operand:HI 1 "general_operand" "g"))]
|
||
+ "noreturn_functionp (operands[0])"
|
||
+ "jmp\t%0"
|
||
+ [(set_attr "length" "3")])
|
||
+
|
||
+
|
||
+;;
|
||
+;; Same as above, but for functions that do return a value.
|
||
+;;
|
||
+(define_expand "call_value"
|
||
+ [(set (match_operand 0 "" "")
|
||
+ (call (match_operand:HI 1 "memory_operand" "")
|
||
+ (match_operand:HI 2 "general_operand" "")))]
|
||
+ ""
|
||
+ "")
|
||
+
|
||
+
|
||
+(define_insn "*call_value_far"
|
||
+ [(set (match_operand 0 "" "=gz")
|
||
+ (call (match_operand:HI 1 "memory_operand" "m")
|
||
+ (match_operand:HI 2 "general_operand" "g")))]
|
||
+ "far_functionp (operands[1])"
|
||
+{
|
||
+ output_far_call_insn (operands, 1);
|
||
+ return "";
|
||
+}
|
||
+ [(set_attr "length" "6")])
|
||
+
|
||
+
|
||
+(define_insn "*call_value_pic"
|
||
+ [(set (match_operand 0 "" "=gz")
|
||
+ (call (mem:HI (match_operand:HI 1 "symbolic_operand" ""))
|
||
+ (match_operand:HI 2 "general_operand" "g")))]
|
||
+ "flag_pic"
|
||
+ "lbsr\t%C1"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_insn "*call_value_nopic"
|
||
+ [(set (match_operand 0 "" "=gz")
|
||
+ (call (match_operand:HI 1 "memory_operand" "m")
|
||
+ (match_operand:HI 2 "general_operand" "g")))]
|
||
+ ""
|
||
+ "jsr\t%1"
|
||
+ [(set_attr "length" "3")
|
||
+ (set (attr "cycles") (const_int JSR_EXTENDED_CYCLES))])
|
||
+
|
||
+
|
||
+
|
||
+;;
|
||
+;; How to generate an untyped call.
|
||
+;;
|
||
+(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, NULL, 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));
|
||
+ }
|
||
+ emit_insn (gen_blockage ());
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "sibcall"
|
||
+ [(parallel
|
||
+ [(call (match_operand:HI 0 "memory_operand" "")
|
||
+ (match_operand:HI 1 "immediate_operand" ""))
|
||
+ (use (reg:HI HARD_PC_REGNUM))])]
|
||
+ ""
|
||
+ "")
|
||
+
|
||
+(define_insn "*sibcall_1"
|
||
+ [(parallel
|
||
+ [(call (match_operand:HI 0 "memory_operand" "m")
|
||
+ (match_operand:HI 1 "immediate_operand" "i"))
|
||
+ (use (reg:HI HARD_PC_REGNUM))])]
|
||
+ "SIBLING_CALL_P(insn)"
|
||
+ "jmp\t%0"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+(define_expand "sibcall_value"
|
||
+ [(parallel
|
||
+ [(set (match_operand 0 "" "")
|
||
+ (call (match_operand:HI 1 "memory_operand" "")
|
||
+ (match_operand:HI 2 "immediate_operand" "")))
|
||
+ (use (reg:HI HARD_PC_REGNUM))])]
|
||
+ ""
|
||
+ "")
|
||
+
|
||
+(define_insn "*sibcall_value_1"
|
||
+ [(parallel
|
||
+ [(set (match_operand 0 "" "=gz")
|
||
+ (call (match_operand:HI 1 "memory_operand" "m")
|
||
+ (match_operand:HI 2 "immediate_operand" "i")))
|
||
+ (use (reg:HI HARD_PC_REGNUM))])]
|
||
+ "SIBLING_CALL_P(insn)"
|
||
+ "jmp\t%1"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Function Entry and Exit
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;; On entry to a function, the stack frame looks as follows:
|
||
+;; - return address (pushed by the caller)
|
||
+;; - saved registers
|
||
+;; - local variable storage
|
||
+;;
|
||
+;; If the function does not modify the stack after that, then
|
||
+;; any of these can be accessed directly as an offset from
|
||
+;; STACK_POINTER_REGNUM. Otherwise, a frame pointer is required.
|
||
+;; In that case, the prologue must also initialize HARD_FRAME_POINTER_REGNUM
|
||
+;; and all references to the stack frame will use that as a base instead.
|
||
+;;
|
||
+(define_expand "prologue"
|
||
+ [(const_int 0)]
|
||
+ "prologue_epilogue_required ()"
|
||
+{
|
||
+ emit_prologue_insns ();
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+;; The function epilogue does exactly the reverse of the prologue,
|
||
+;; deallocating local variable space, restoring saved registers,
|
||
+;; and returning.
|
||
+;;
|
||
+;; For the 6809, the return may be 'rti' if the function was
|
||
+;; declared as an interrupt function, but is normally 'rts'.
|
||
+;;
|
||
+;; Also, as an optimization, the register restore and the 'rts'
|
||
+;; can be combined into a single instruction, by adding 'PC' to the
|
||
+;; list of registers to be restored. This is only done if there are
|
||
+;; any saved registers, as 'rts' is more efficient by itself.
|
||
+;;
|
||
+(define_expand "epilogue"
|
||
+ [(const_int 0)]
|
||
+ "prologue_epilogue_required ()"
|
||
+{
|
||
+ emit_epilogue_insns (false);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+(define_expand "sibcall_epilogue"
|
||
+ [(const_int 0)]
|
||
+ "prologue_epilogue_required ()"
|
||
+{
|
||
+ emit_epilogue_insns (true);
|
||
+ DONE;
|
||
+})
|
||
+
|
||
+
|
||
+;; The RTS instruction
|
||
+(define_insn "return_rts"
|
||
+ [(return)
|
||
+ (use (reg:HI HARD_PC_REGNUM))]
|
||
+ "!m6809_current_function_has_type_attr_p (\"interrupt\")
|
||
+ && m6809_get_live_regs () == 0"
|
||
+ "rts"
|
||
+ [(set_attr "length" "1")
|
||
+ (set (attr "cycles") (const_int RTS_CYCLES))])
|
||
+
|
||
+(define_insn "return_puls_pc"
|
||
+ [(return)
|
||
+ (use (reg:HI HARD_PC_REGNUM))]
|
||
+ "!m6809_current_function_has_type_attr_p (\"interrupt\")
|
||
+ && m6809_get_live_regs () != 0"
|
||
+ ""
|
||
+ [(set_attr "length" "1")
|
||
+ (set (attr "cycles") (const_int RTS_CYCLES))])
|
||
+
|
||
+;; The RTI instruction
|
||
+(define_insn "return_rti"
|
||
+ [(return)
|
||
+ (use (reg:HI HARD_PC_REGNUM))]
|
||
+ "m6809_current_function_has_type_attr_p (\"interrupt\")"
|
||
+ "rti"
|
||
+ [(set_attr "length" "1")
|
||
+ (set (attr "cycles") (const_int RTI_CYCLES))])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Unspecified instructions
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;; An instruction that has the effect of an unspec_volatile, but
|
||
+;; which doesn't require emitting any assembly code.
|
||
+(define_insn "blockage"
|
||
+ [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
|
||
+ ""
|
||
+ ""
|
||
+ [(set_attr "length" "0")
|
||
+ (set (attr "cycles") (const_int 0))])
|
||
+
|
||
+
|
||
+;; Say how to push multiple registers onto the stack, using
|
||
+;; the 6809 'pshs' instruction. The operand is a regset
|
||
+;; specifying which registers to push.
|
||
+;;
|
||
+;; The operand mode is not given intentionally, so as to allow
|
||
+;; any possible integer mode for the regset.
|
||
+;;
|
||
+;; See below for a peephole that can combine consecutive push
|
||
+;; instructions that qualify for merging.
|
||
+(define_insn "register_push"
|
||
+ [(use (reg:HI HARD_S_REGNUM))
|
||
+ (unspec_volatile
|
||
+ [(match_operand 0 "immediate_operand" "")] UNSPEC_PUSH_RS)
|
||
+ (clobber (reg:HI HARD_S_REGNUM))]
|
||
+ ""
|
||
+ "pshs\t%R0"
|
||
+ [(set_attr "length" "2")
|
||
+ (set (attr "cycles") (const_int PSH_PUL_CYCLES))])
|
||
+
|
||
+
|
||
+;; Say how to pop multiple registers from the stack, using
|
||
+;; the 6809 'puls' instruction. The operand is the register
|
||
+;; bitset value.
|
||
+(define_insn "register_pop"
|
||
+ [(use (reg:HI HARD_S_REGNUM))
|
||
+ (unspec_volatile
|
||
+ [(match_operand 0 "immediate_operand" "")] UNSPEC_POP_RS)
|
||
+ (clobber (reg:HI HARD_S_REGNUM))]
|
||
+ ""
|
||
+ "puls\t%R0"
|
||
+ [(set_attr "length" "2")
|
||
+ (set (attr "cycles") (const_int PSH_PUL_CYCLES))])
|
||
+
|
||
+
|
||
+(define_insn "m6809_swi"
|
||
+ [(unspec_volatile
|
||
+ [(match_operand:QI 0 "immediate_operand" "I,n")] UNSPEC_SWI)]
|
||
+ ""
|
||
+ "@
|
||
+ swi
|
||
+ swi%c0"
|
||
+ [(set_attr "length" "1,2")
|
||
+ (set (attr "cycles") (const_int SWI_CYCLES))])
|
||
+
|
||
+
|
||
+;; Generate the CWAI instruction
|
||
+(define_insn "m6809_cwai"
|
||
+ [(unspec_volatile
|
||
+ [(match_operand:QI 0 "immediate_operand" "")] UNSPEC_CWAI)]
|
||
+ ""
|
||
+ "cwai\t%0"
|
||
+ [(set_attr "length" "2")
|
||
+ (set (attr "cycles") (const_int CWAI_CYCLES))])
|
||
+
|
||
+
|
||
+;; Generate the SYNC instruction
|
||
+(define_insn "m6809_sync"
|
||
+ [(unspec_volatile [(const_int 0)] UNSPEC_SYNC)]
|
||
+ ""
|
||
+ "sync"
|
||
+ [(set_attr "length" "1")
|
||
+ (set (attr "cycles") (const_int SYNC_CYCLES))])
|
||
+
|
||
+
|
||
+;; Generate the NOP instruction
|
||
+(define_insn "nop"
|
||
+ [(const_int 0)]
|
||
+ ""
|
||
+ "nop"
|
||
+ [(set_attr "length" "1")
|
||
+ (set (attr "cycles") (const_int NOP_CYCLES))])
|
||
+
|
||
+
|
||
+;;--------------------------------------------------------------------
|
||
+;;- Peepholes
|
||
+;;--------------------------------------------------------------------
|
||
+
|
||
+;;; Each peephole has an ID that is used for debugging.
|
||
+;;; Each peephole condition is bracketed by calls to
|
||
+;;; m6809_match_peephole2() also for debugging.
|
||
+(define_constants [
|
||
+ (PEEP_END 0)
|
||
+ (PEEP_COND 1)
|
||
+
|
||
+ (PEEP_STACK_STORE_INC 0)
|
||
+ (PEEP_STACK_CLEAR_INC 1)
|
||
+ (PEEP_LSRB_ADCB 2)
|
||
+ (PEEP_ABX 3)
|
||
+ (PEEP_ABX2 4)
|
||
+ (PEEP_INDEXED_INC 5)
|
||
+ (PEEP_MEM_DEC 6)
|
||
+ (PEEP_MEM_INC 7)
|
||
+ (PEEP_MEM_DEC_CMP 8)
|
||
+ (PEEP_PUSH2 9)
|
||
+ (PEEP_STORE_IMPLIES_CC 10)
|
||
+ (PEEP_DEC_IMPLIES_CC 11)
|
||
+ (PEEP_LEAB 12)
|
||
+ (PEEP_LDX_INDIRECT 13)
|
||
+ (PEEP_POP_JUNK 14)
|
||
+])
|
||
+
|
||
+
|
||
+;;; Optimize 'leas -1,s' followed by 'stb ,s'. This can happen if the
|
||
+;;; function prologue needs to allocate stack space and 'b' is placed
|
||
+;;; into that local right away. Combine the stack allocation with the
|
||
+;;; store using preincrement mode.
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_S_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_S_REGNUM) (const_int -1)))
|
||
+ (set (mem:QI (reg:HI HARD_S_REGNUM))
|
||
+ (match_operand:QI 0 "register_operand" ""))]
|
||
+ "m6809_match_peephole2 (PEEP_STACK_STORE_INC, PEEP_END)"
|
||
+ [(set (mem:QI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 0))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Same as above, but for a 'clr ,s' that follows the prologue.
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_S_REGNUM) (plus:HI (reg:HI HARD_S_REGNUM) (const_int -1)))
|
||
+ (set (mem:QI (reg:HI HARD_S_REGNUM)) (const_int 0))]
|
||
+ "m6809_match_peephole2 (PEEP_STACK_CLEAR_INC, PEEP_END)"
|
||
+ [(set (mem:QI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (const_int 0))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Merge two consecutive push instructions into a single register_push.
|
||
+(define_peephole2
|
||
+ [(set (match_operand 0 "push_operand" "")
|
||
+ (match_operand 1 "register_operand" ""))
|
||
+ (set (match_operand 2 "push_operand" "")
|
||
+ (match_operand 3 "register_operand" ""))]
|
||
+ "m6809_match_peephole2 (PEEP_PUSH2, PEEP_COND)
|
||
+ && reload_completed
|
||
+ && GET_MODE (operands[1]) == GET_MODE (operands[3])
|
||
+ && m6809_can_merge_pushpop_p (UNSPEC_PUSH_RS, 1 << REGNO (operands[1]), 1 << REGNO (operands[3]))
|
||
+ && m6809_match_peephole2 (PEEP_PUSH2, PEEP_END)"
|
||
+ [(parallel [
|
||
+ (use (reg:HI HARD_S_REGNUM))
|
||
+ (unspec_volatile [(match_dup 4)] UNSPEC_PUSH_RS)
|
||
+ (clobber (reg:HI HARD_S_REGNUM))])
|
||
+ (use (match_dup 1))
|
||
+ (use (match_dup 3))]
|
||
+{
|
||
+ operands[4] = gen_rtx_CONST_INT (QImode,
|
||
+ (1 << REGNO (operands[1])) | (1 << REGNO (operands[3])));
|
||
+})
|
||
+
|
||
+
|
||
+;;; Convert 'stX ,--s' into a push instruction. Use the regset
|
||
+;;; notation, so that it may be combined with an adjacent regset.
|
||
+;;; TBD - this doesn't compile some code cleanly.
|
||
+;(define_peephole2
|
||
+; [(set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM)))
|
||
+; (reg:HI HARD_X_REGNUM))]
|
||
+; "reload_completed"
|
||
+; [(parallel [
|
||
+; (use (reg:HI HARD_S_REGNUM))
|
||
+; (unspec_volatile [(match_dup 0)] UNSPEC_PUSH_RS)
|
||
+; (clobber (reg:HI HARD_S_REGNUM))])]
|
||
+;{
|
||
+; operands[0] = gen_rtx_CONST_INT (HImode, X_REGBIT);
|
||
+;})
|
||
+
|
||
+
|
||
+;;;
|
||
+;;; q = (q+1)/2 can be optimized as "lsrb; adcb". This also
|
||
+;;; won't overflow when q=0xFF.
|
||
+;;; TODO : this form isn't accounting for promotion when
|
||
+;;; using 16-bit ints.
|
||
+;;;
|
||
+(define_peephole
|
||
+ [(set (reg:QI HARD_D_REGNUM)
|
||
+ (lshiftrt:QI (plus:HI (match_dup 0) (const_int 1)) (const_int 1)))]
|
||
+ "m6809_match_peephole2 (PEEP_LSRB_ADCB, PEEP_END)"
|
||
+ "lsrb\;adcb\t#0; peephole"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+;;
|
||
+;; Optimize the case of following a register store with a test
|
||
+;; of reg or mem just moved.
|
||
+;;
|
||
+(define_peephole
|
||
+ [(set (match_operand:HI 0 "memory_operand" "=m")
|
||
+ (match_operand:HI 1 "register_operand" "r"))
|
||
+ (set (cc0) (match_operand:HI 2 "general_operand" "g"))]
|
||
+ "m6809_match_peephole2 (PEEP_STORE_IMPLIES_CC, PEEP_COND)
|
||
+ && (operands[2] == operands[0] || operands[2] == operands[1])
|
||
+ && m6809_match_peephole2 (PEEP_STORE_IMPLIES_CC, PEEP_END)"
|
||
+ "st%1\t%0\t;movhi: R:%1 -> %0 w/ implied test of %2"
|
||
+ [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+;; Optimize a pair of SET instructions in which the second insn
|
||
+;; is the reverse of the first one. I.e.
|
||
+;;
|
||
+;; A = B
|
||
+;; ----> A = B
|
||
+;; B = A
|
||
+;;
|
||
+;; The second insn is redundant. Define two patterns, one for QI, one for HI.
|
||
+;; But don't do this if either is a VOLATILE MEM.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
|
||
+ (match_operand:HI 1 "nonimmediate_operand" ""))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "!MEM_P (operands[0]) || !MEM_P (operands[1]) || (!MEM_VOLATILE_P (operands[0]) && !MEM_VOLATILE_P (operands[1]))"
|
||
+ [(set (match_dup 0) (match_dup 1))]
|
||
+ "")
|
||
+
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
|
||
+ (match_operand:QI 1 "nonimmediate_operand" ""))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "!MEM_P (operands[0]) || !MEM_P (operands[1]) || (!MEM_VOLATILE_P (operands[0]) && !MEM_VOLATILE_P (operands[1]))"
|
||
+ [(set (match_dup 0) (match_dup 1))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;
|
||
+;; Optimize the sum of an 8-bit and 16-bit using the 'abx' instruction
|
||
+;; if B and X can be used. Two patterns are provided to catch both
|
||
+;; X=X+D and X=D+X.
|
||
+;;
|
||
+(define_peephole
|
||
+ [(set (reg:HI HARD_D_REGNUM)
|
||
+ (zero_extend:HI (match_operand:QI 0 "general_operand" "q")))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_D_REGNUM) (reg:HI HARD_X_REGNUM)))]
|
||
+ "m6809_match_peephole2 (PEEP_ABX, PEEP_END)"
|
||
+ "abx"
|
||
+ [(set_attr "length" "1")])
|
||
+
|
||
+(define_peephole
|
||
+ [(set (reg:HI HARD_D_REGNUM)
|
||
+ (zero_extend:HI (match_operand:QI 0 "general_operand" "q")))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_X_REGNUM) (reg:HI HARD_D_REGNUM)))]
|
||
+ "m6809_match_peephole2 (PEEP_ABX, PEEP_END)"
|
||
+ "abx"
|
||
+ [(set_attr "length" "1")])
|
||
+
|
||
+;;; Likewise, handle when B is scaled by 2 prior to the add.
|
||
+;;; Instead of shifting B in 4 cycles, just do the ABX a second
|
||
+;;; time, in only 3 cycles.
|
||
+
|
||
+(define_peephole
|
||
+ [(set (reg:HI HARD_D_REGNUM)
|
||
+ (zero_extend:HI (match_operand:QI 0 "general_operand" "q")))
|
||
+ (set (reg:HI HARD_D_REGNUM)
|
||
+ (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 1)))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_D_REGNUM) (reg:HI HARD_X_REGNUM)))]
|
||
+ "m6809_match_peephole2 (PEEP_ABX2, PEEP_END)"
|
||
+ "abx\;abx"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+(define_peephole
|
||
+ [(set (reg:HI HARD_D_REGNUM)
|
||
+ (zero_extend:HI (match_operand:QI 0 "general_operand" "q")))
|
||
+ (set (reg:HI HARD_D_REGNUM)
|
||
+ (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 1)))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (plus:HI (reg:HI HARD_X_REGNUM) (reg:HI HARD_D_REGNUM)))]
|
||
+ "m6809_match_peephole2 (PEEP_ABX2, PEEP_END)"
|
||
+ "abx\;abx"
|
||
+ [(set_attr "length" "2")])
|
||
+
|
||
+
|
||
+;;
|
||
+;; Work around a compiler bug that generates bad code when copying
|
||
+;; between 32-bit memory addresses after a libcall. The problem seen is
|
||
+;; that the source is MEM (REG X), but X is used as the reload register.
|
||
+;; The second half of the copy therefore fails.
|
||
+;;
|
||
+;; The solution is to switch the reload register to D, since that is guaranteed
|
||
+;; not to be in use right after a libcall.
|
||
+;;
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_X_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM)))
|
||
+ (set (match_operand:HI 0 "nonimmediate_operand" "") (reg:HI HARD_X_REGNUM))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (mem:HI (plus:HI (reg:HI HARD_X_REGNUM) (const_int 2))))
|
||
+ (set (match_operand:HI 1 "nonimmediate_operand" "") (reg:HI HARD_X_REGNUM))]
|
||
+ "reload_completed"
|
||
+ [(set (reg:HI HARD_D_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM)))
|
||
+ (set (match_dup 0) (reg:HI HARD_D_REGNUM))
|
||
+ (set (reg:HI HARD_X_REGNUM)
|
||
+ (mem:HI (plus:HI (reg:HI HARD_X_REGNUM) (const_int 2))))
|
||
+ (set (match_dup 1) (reg:HI HARD_X_REGNUM))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;; Turn "and then test" into a "bit test" operation.
|
||
+;; Provide variants for immediate and memory sources
|
||
+;; This is the most used peephople.
|
||
+; (define_peephole
|
||
+; [(set (match_operand:QI 0 "register_operand" "=q")
|
||
+; (and:QI (match_operand:QI 1 "register_operand" "0")
|
||
+; (match_operand:QI 2 "immediate_operand" "i")))
|
||
+; (set (cc0) (match_dup 0))]
|
||
+; ""
|
||
+; "bit%0\t%2"
|
||
+; [(set_attr "length" "3")])
|
||
+;
|
||
+; (define_peephole
|
||
+; [(set (match_operand:QI 0 "register_operand" "=q")
|
||
+; (and:QI (match_operand:QI 1 "register_operand" "0")
|
||
+; (match_operand:QI 2 "memory_operand" "m")))
|
||
+; (set (cc0) (match_dup 0))]
|
||
+; ""
|
||
+; "bit%0\t%2"
|
||
+; [(set_attr "length" "4")])
|
||
+
|
||
+
|
||
+;; Turn a "decrement, then test" sequence into just a "decrement".
|
||
+;; The test can be omitted, since it is implicitly done.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
|
||
+ (plus:QI (match_operand:QI 1 "whole_general_operand" "")
|
||
+ (match_operand:QI 2 "immediate_operand" "")))
|
||
+ (set (cc0) (match_dup 0))]
|
||
+ "m6809_match_peephole2 (PEEP_DEC_IMPLIES_CC, PEEP_END)"
|
||
+ [(set (match_dup 0) (plus:QI (match_dup 1) (match_dup 2)))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;; Merge an indexed register increment with a previous usage.
|
||
+;; This is usually done automatically, but not always
|
||
+;; The 'use' should be optional; in all cases where this has been
|
||
+;; seen, it is required though.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (mem:QI (match_operand:HI 1 "index_register_operand" "")))
|
||
+ (use (match_dup 0))
|
||
+ (set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))]
|
||
+ "m6809_match_peephole2 (PEEP_INDEXED_INC, PEEP_END)"
|
||
+ [(set (match_dup 0) (mem:QI (post_inc:HI (match_dup 1))))
|
||
+ (use (match_dup 0))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Merge "ldX MEM; ldX ,X" into a single instruction using
|
||
+;;; the indirect mode.
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_X_REGNUM)
|
||
+ (mem:HI (match_operand:HI 0 "general_operand" "")))
|
||
+ (set (reg:HI HARD_X_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM)))]
|
||
+ "reload_completed && m6809_match_peephole2 (PEEP_LDX_INDIRECT, PEEP_END)"
|
||
+ [(set (reg:HI HARD_X_REGNUM)
|
||
+ (mem:HI (mem:HI (match_dup 0))))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Reorder a store followed by a unary operation on that memory
|
||
+;;; so that the unary is performed and then the store. Consider
|
||
+;;; a binary shift operation, which will be decomposed into
|
||
+;;; identical single shifts, also.
|
||
+;;; TODO - recognize more than just 'ashift' here.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "memory_operand" "")
|
||
+ (match_operand:QI 1 "register_operand" ""))
|
||
+ (set (match_dup 0)
|
||
+ (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand")))]
|
||
+ "reload_completed"
|
||
+ [(set (match_dup 1)
|
||
+ (ashift:QI (match_dup 1) (match_operand:QI 2 "immediate_operand")))
|
||
+ (set (match_dup 0) (match_dup 1))]
|
||
+ "")
|
||
+
|
||
+;;; Likewise, reorder a unary MEM followed by a load, so that the load
|
||
+;;; is done first, then use the REG instead of the MEM.
|
||
+;;;(define_peephole2
|
||
+;;; [(set (match_dup 0)
|
||
+;;; (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand")))
|
||
+;;; (set (match_operand:QI 0 "register_operand" "")
|
||
+;;; (match_operand:QI 1 "memory_operand" ""))]
|
||
+;;; "reload_completed"
|
||
+;;; [(set (match_dup 0) (match_dup 1))
|
||
+;;; (set (match_dup 0)
|
||
+;;; (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand")))]
|
||
+;;; "")
|
||
+
|
||
+
|
||
+;;; Replace sex; leaX d,Y with leaX b,Y.
|
||
+;;;
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM)))
|
||
+ (set (match_operand:HI 0 "index_register_operand" "")
|
||
+ (plus:HI (match_operand:HI 1 "index_register_operand" "")
|
||
+ (reg:HI HARD_D_REGNUM)))]
|
||
+ "reload_completed && m6809_match_peephole2 (PEEP_LEAB, PEEP_END)"
|
||
+ [(set (match_dup 0)
|
||
+ (plus:HI (match_dup 1) (reg:QI HARD_D_REGNUM)))]
|
||
+ "")
|
||
+
|
||
+(define_peephole2
|
||
+ [(set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM)))
|
||
+ (set (match_operand:HI 0 "index_register_operand" "")
|
||
+ (plus:HI (reg:HI HARD_D_REGNUM)
|
||
+ (match_operand:HI 1 "index_register_operand" "")))]
|
||
+ "reload_completed && m6809_match_peephole2 (PEEP_LEAB, PEEP_END)"
|
||
+ [(set (match_dup 0)
|
||
+ (plus:HI (match_dup 1) (reg:QI HARD_D_REGNUM)))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Replace ldb; decb; stb; tstb with dec(mem). If the
|
||
+;;; register is not needed, then the load will get deleted
|
||
+;;; automatically, but it may be needed for comparisons.
|
||
+;;; Same for incb/inc.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (match_operand:QI 1 "nonimmediate_operand" ""))
|
||
+ (set (match_dup 0) (plus:QI (match_dup 0) (const_int -1)))
|
||
+ (set (match_dup 1) (match_dup 0))
|
||
+ (set (cc0) (match_dup 0))]
|
||
+ "m6809_match_peephole2 (PEEP_MEM_DEC_CMP, PEEP_END)"
|
||
+ [(set (match_dup 1) (plus:QI (match_dup 1) (const_int -1)))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Replace ldb; decb; stb with dec(mem); ldb. If the
|
||
+;;; register is not needed, then the load will get deleted
|
||
+;;; automatically, but it may be needed for comparisons.
|
||
+;;; Same for incb/inc.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (match_operand:QI 1 "nonimmediate_operand" ""))
|
||
+ (set (match_dup 0) (plus:QI (match_dup 0) (const_int -1)))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "m6809_match_peephole2 (PEEP_MEM_DEC, PEEP_END)"
|
||
+ [(set (match_dup 1) (plus:QI (match_dup 1) (const_int -1)))
|
||
+ (set (match_dup 0) (match_dup 1))]
|
||
+ "")
|
||
+
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (match_operand:QI 1 "nonimmediate_operand" ""))
|
||
+ (set (match_dup 0) (plus:QI (match_dup 0) (const_int 1)))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "m6809_match_peephole2 (PEEP_MEM_INC, PEEP_END)"
|
||
+ [(set (match_dup 1) (plus:QI (match_dup 1) (const_int 1)))
|
||
+ (set (match_dup 0) (match_dup 1))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Replace "andb #N; cmpb #N; bhi" with "andb #N", if it can be proven
|
||
+;;; that the branch can never occur because of the limited range of B.
|
||
+;;; N must be a power of two for this to make sense. This helps with
|
||
+;;; the default cases of switch statements on a value (x & N).
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (and:QI (match_dup 0) (match_operand:QI 1 "immediate_operand" "")))
|
||
+ (set (cc0)
|
||
+ (compare (match_dup 0) (match_dup 1)))
|
||
+ (set (pc) (if_then_else (gtu (cc0) (const_int 0)) (match_operand 2 "" "") (match_operand 3 "" "")))
|
||
+ ]
|
||
+ "reload_completed && power_of_two_p (INTVAL (operands[1]) + 1)"
|
||
+ [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1)))]
|
||
+ "")
|
||
+
|
||
+;;; Replace ldd <mem>; addd #1; std <mem> with 16-bit increment
|
||
+;;; of the mem, but only if D is dead. Same for 16-bit decrement.
|
||
+;;; <mem> must be offsettable for the instruction to match.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:HI 0 "register_operand" "") (match_operand:HI 1 "memory_operand" ""))
|
||
+ (set (match_dup 0) (plus:HI (match_dup 0) (const_int 1)))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "reload_completed
|
||
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
|
||
+ && peep2_reg_dead_p (3, operands[0])"
|
||
+ [(set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))]
|
||
+ "")
|
||
+
|
||
+(define_peephole2
|
||
+ [(set (match_operand:HI 0 "register_operand" "") (match_operand:HI 1 "memory_operand" ""))
|
||
+ (set (match_dup 0) (plus:HI (match_dup 0) (const_int -1)))
|
||
+ (set (match_dup 1) (match_dup 0))]
|
||
+ "reload_completed
|
||
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
|
||
+ && peep2_reg_dead_p (3, operands[0])"
|
||
+ [(set (match_dup 1) (plus:HI (match_dup 1) (const_int -1)))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;; Replace a load or store using an indexed register, followed by an increment of that
|
||
+;;; register, with the combined form using autoincrement.
|
||
+(define_peephole2
|
||
+ [(set (match_operand:QI 0 "register_operand" "")
|
||
+ (mem:QI (match_operand:HI 1 "index_register_operand" "")))
|
||
+ (set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))]
|
||
+ "reload_completed"
|
||
+ [(set (match_dup 0) (mem:QI (post_inc (match_dup 1))))]
|
||
+ "")
|
||
+
|
||
+
|
||
+;;- mode:emacs-lisp
|
||
+;;- comment-start: ";;- "
|
||
+;;- eval: (set-syntax-table (copy-sequence (syntax-table)))
|
||
+;;- eval: (modify-syntax-entry ?[ "(]")
|
||
+;;- eval: (modify-syntax-entry ?] ")[")
|
||
+;;- eval: (modify-syntax-entry ?{ "(}")
|
||
+;;- eval: (modify-syntax-entry ?} "){")
|
||
+;-; vim: set ts=2:
|
||
+;-; vim: set expandtab:
|
||
+;-; vim: set filetype=lisp:
|
||
+;;- End:
|
||
diff --git a/gcc/config/m6809/m6809.opt b/gcc/config/m6809/m6809.opt
|
||
new file mode 100644
|
||
index 0000000..6270917
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/m6809.opt
|
||
@@ -0,0 +1,98 @@
|
||
+; Options for the M6809 port of the compiler
|
||
+;
|
||
+; Copyright (C) 2005 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.
|
||
+;
|
||
+; 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 COPYING. If not, write to the Free
|
||
+; Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
||
+; 02110-1301, USA.
|
||
+
|
||
+margcount
|
||
+Target Mask(ARGCOUNT)
|
||
+Push argument count
|
||
+
|
||
+mint8
|
||
+Target RejectNegative Mask(BYTE_INT)
|
||
+Use 8-bit integers
|
||
+
|
||
+mint16
|
||
+Target RejectNegative
|
||
+Use 16-bit integers InverseMask(BYTE_INT)
|
||
+
|
||
+mreg-args
|
||
+Target Mask(REG_ARGS)
|
||
+Use registers for function arguments
|
||
+
|
||
+mshort_size
|
||
+Target RejectNegative Mask(SMALL_SIZE_T)
|
||
+Use 8-bit size_t
|
||
+
|
||
+mlong_size
|
||
+Target RejectNegative InverseMask(SMALL_SIZE_T)
|
||
+Use 16-bit size_t
|
||
+
|
||
+mdirect
|
||
+Target Mask(DIRECT)
|
||
+Enable direct addressing
|
||
+
|
||
+mwpc
|
||
+Target RejectNegative Mask(WPC)
|
||
+Enable WPC platform extensions
|
||
+
|
||
+mexperiment
|
||
+Target RejectNegative Mask(EXPERIMENT)
|
||
+Enable current experimental feature
|
||
+
|
||
+m6309
|
||
+Target RejectNegative Mask(6309)
|
||
+Enable Hitachi 6309 extensions
|
||
+
|
||
+mcasesi
|
||
+Target RejectNegative Mask(CASESI)
|
||
+Enable the casesi pattern
|
||
+
|
||
+mfar-code-page=
|
||
+Target RejectNegative Joined Var(far_code_page_option)
|
||
+Sets the far code page value for this compilation unit
|
||
+
|
||
+mcode-section=
|
||
+Target RejectNegative Joined Var(code_section_ptr)
|
||
+Sets the name of the section for code
|
||
+
|
||
+mdata-section=
|
||
+Target RejectNegative Joined Var(data_section_ptr)
|
||
+Sets the name of the section for initialized data
|
||
+
|
||
+mbss-section=
|
||
+Target RejectNegative Joined Var(bss_section_ptr)
|
||
+Sets the name of the section for uninitialized data
|
||
+
|
||
+mabi_version=
|
||
+Target RejectNegative Joined Var(m6809_abi_version_ptr)
|
||
+Sets the calling convention
|
||
+
|
||
+msoft-reg-count=
|
||
+Target RejectNegative Joined Var(m6809_soft_reg_count)
|
||
+Sets the number of soft registers that can be used
|
||
+
|
||
+mdret
|
||
+Target RejectNegative Mask(DRET)
|
||
+Put function call results in D, not X
|
||
+
|
||
+mfar-stack-param
|
||
+Target Mask(FAR_STACK_PARAM)
|
||
+Enable stack parameters to a farcall
|
||
+
|
||
+
|
||
diff --git a/gcc/config/m6809/predicates.md b/gcc/config/m6809/predicates.md
|
||
new file mode 100644
|
||
index 0000000..0e257ec
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/predicates.md
|
||
@@ -0,0 +1,78 @@
|
||
+;; Predicate definitions for Motorola 6809
|
||
+;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||
+;;
|
||
+;; This file is part of GCC.
|
||
+;;
|
||
+;; GCC is free software; you can redistribute it and/or modify
|
||
+;; it under the terms of the GNU General Public License as published by
|
||
+;; the Free Software Foundation; either version 3, or (at your option)
|
||
+;; any later version.
|
||
+;;
|
||
+;; 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/>.
|
||
+
|
||
+;; whole_register_operand is like register_operand, but it
|
||
+;; does not allow SUBREGs.
|
||
+(define_predicate "whole_register_operand"
|
||
+ (and (match_code "reg")
|
||
+ (match_operand 0 "register_operand")))
|
||
+
|
||
+
|
||
+;; A predicate that matches any index register. This can be used in nameless
|
||
+;; patterns and peepholes which need a 16-bit reg, but not D.
|
||
+(define_predicate "index_register_operand"
|
||
+ (and (match_code "reg")
|
||
+ (match_test "REGNO (op) == HARD_X_REGNUM || REGNO (op) == HARD_Y_REGNUM || REGNO (op) == HARD_U_REGNUM")))
|
||
+
|
||
+
|
||
+;; match only X
|
||
+(define_predicate "register_operand_x"
|
||
+ (and (match_code "reg")
|
||
+ (match_test "REGNO (op) == HARD_X_REGNUM")))
|
||
+
|
||
+;; match only D
|
||
+(define_predicate "register_operand_d"
|
||
+ (and (match_code "reg")
|
||
+ (match_test "REGNO (op) == HARD_D_REGNUM")))
|
||
+
|
||
+
|
||
+;; Likwise, a replacement for general_operand which excludes
|
||
+;; SUBREGs.
|
||
+(define_predicate "whole_general_operand"
|
||
+ (and (match_code "const_int,const_double,const,symbol_ref,label_ref,reg,mem")
|
||
+ (match_operand 0 "general_operand")))
|
||
+
|
||
+
|
||
+(define_predicate "add_general_operand"
|
||
+ (and (match_code "const_int,const_double,const,symbol_ref,label_ref,reg,mem")
|
||
+ (match_operand 0 "general_operand")
|
||
+ (match_test "REGNO (op) != SOFT_AP_REGNUM")))
|
||
+
|
||
+
|
||
+(define_predicate "shift_count_operand"
|
||
+ (and (match_code "const_int")
|
||
+ (and (match_operand 0 "const_int_operand")
|
||
+ (match_test "INTVAL (op) == 1 || INTVAL (op) == 8"))))
|
||
+
|
||
+
|
||
+;; A predicate that matches any bitwise logical operator. This
|
||
+;; allows for a single RTL pattern to be used for multiple operations.
|
||
+(define_predicate "logical_bit_operator"
|
||
+ (ior (match_code "and") (match_code "ior") (match_code "xor")))
|
||
+
|
||
+
|
||
+;; A predicate that matches any shift or rotate operator. This
|
||
+;; allows for a single RTL pattern to be used for multiple operations.
|
||
+(define_predicate "shift_rotate_operator"
|
||
+ (ior (match_code "ashift") (match_code "ashiftrt") (match_code "lshiftrt")
|
||
+ (match_code "rotate") (match_code "rotatert")))
|
||
+
|
||
+
|
||
+(define_predicate "symbolic_operand" (match_code "symbol_ref"))
|
||
+
|
||
diff --git a/gcc/config/m6809/t-coco b/gcc/config/m6809/t-coco
|
||
new file mode 100644
|
||
index 0000000..b1fa507
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/t-coco
|
||
@@ -0,0 +1,6 @@
|
||
+# For a few minor differences in code generation on the CoCo...
|
||
+T_CFLAGS = -DTARGET_COCO
|
||
+
|
||
+# For doing the startup differently on the CoCo...
|
||
+CRT0STUFF_T_CFLAGS += -Wa,--globalize-symbols -DTARGET_COCO
|
||
+# vim: set filetype=make:
|
||
diff --git a/gcc/config/m6809/t-m6809 b/gcc/config/m6809/t-m6809
|
||
new file mode 100644
|
||
index 0000000..2b38a8f
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/t-m6809
|
||
@@ -0,0 +1,64 @@
|
||
+
|
||
+# ranlib doesn't exist, so define it to 'true' to make it a no-op
|
||
+RANLIB_FOR_TARGET = true
|
||
+
|
||
+# Stubs for libgcc defined by m6809 are here
|
||
+LIB1ASMSRC = m6809/libgcc1.s
|
||
+
|
||
+# Here are the functions that are implemented within libgcc1.s
|
||
+LIB1ASMFUNCS = _mulhi3 _divhi3 _modhi3 _udivhi3 _umodhi3 \
|
||
+ _euclid _seuclid _clzsi2 _clzdi2 _ctzsi2 _ctzdi2 _softregs \
|
||
+ _ashlhi3 _ashrhi3 _lshrhi3
|
||
+
|
||
+# Flags to use when building libgcc. IN_GCC does not seem necessary,
|
||
+# although the compile breaks without it. -DDF=SF is required to set
|
||
+# the size of "double" to the same as the size of a "float".
|
||
+TARGET_LIBGCC2_CFLAGS =-DIN_GCC -Dinhibit_libc -DDF=SF -DLIBGCC2_HAS_SF_MODE=0 -DLIBGCC2_HAS_DF_MODE=0
|
||
+
|
||
+LIB2ADDEH =
|
||
+LIB2ADDEHSTATIC =
|
||
+LIB2ADDEHSHARED =
|
||
+
|
||
+LIBGCC2_DEBUG_CFLAGS =
|
||
+LIBGCC2_CFLAGS = -Os $(LIBGCC2_INCLUDES) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2
|
||
+
|
||
+# Multilib information
|
||
+# This creates multiple versions of libgcc.a for each set of incompatible
|
||
+# -mxxx options.
|
||
+MULTILIB_OPTIONS = fpic mdret
|
||
+MULTILIB_DIRNAMES =
|
||
+MULTILIB_MATCHES =
|
||
+MULTILIB_EXCEPTIONS =
|
||
+EXTRA_MULTILIB_PARTS = crt0.o
|
||
+
|
||
+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
|
||
+
|
||
+fp-bit.c: $(srcdir)/config/fp-bit.c
|
||
+ echo '#define FLOAT' > fp-bit.c
|
||
+ echo '#define FLOAT_ONLY' >> 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
|
||
+ echo '#define DI SI' >> fp-bit.c
|
||
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
|
||
+
|
||
+# crt0.o is built from the following source file
|
||
+CRT0_S = $(srcdir)/config/m6809/crt0.S
|
||
+MCRT0_S = $(srcdir)/config/m6809/crt0.S
|
||
+
|
||
+# Flags to use when building crt0.o
|
||
+CRT0STUFF_T_CFLAGS += -fno-builtin -nostartfiles -nostdlib
|
||
+
|
||
+# Assemble startup files.
|
||
+$(T)crt0.o: $(CRT0_S) $(GCC_PASSES)
|
||
+ $(GCC_FOR_TARGET) $(CRT0STUFF_T_CFLAGS) $(MULTILIB_CFLAGS) -c -o $(T)crt0.o -x assembler-with-cpp $(CRT0_S)
|
||
+
|
||
+$(T)mcrt0.o: $(MCRT0_S) $(GCC_PASSES)
|
||
+ $(GCC_FOR_TARGET) $(CRT0STUFF_T_CFLAGS) $(MULTILIB_CFLAGS) -c -o $(T)mcrt0.o -x assembler-with-cpp $(MCRT0_S)
|
||
diff --git a/gcc/config/m6809/t-sim b/gcc/config/m6809/t-sim
|
||
new file mode 100644
|
||
index 0000000..cd3b094
|
||
--- /dev/null
|
||
+++ b/gcc/config/m6809/t-sim
|
||
@@ -0,0 +1 @@
|
||
+CRT0STUFF_T_CFLAGS += -DTARGET_SIM
|
||
diff --git a/gcc/gcse.c b/gcc/gcse.c
|
||
index 27f7e8f..1079c6f 100644
|
||
--- a/gcc/gcse.c
|
||
+++ b/gcc/gcse.c
|
||
@@ -833,7 +833,6 @@ want_to_gcse_p (rtx x, int *max_distance_ptr)
|
||
max_distance = (GCSE_COST_DISTANCE_RATIO * cost) / 10;
|
||
if (max_distance == 0)
|
||
return 0;
|
||
-
|
||
gcc_assert (max_distance > 0);
|
||
}
|
||
else
|
||
diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c
|
||
index 78d53f0..b5b8c84 100644
|
||
--- a/gcc/libgcc2.c
|
||
+++ b/gcc/libgcc2.c
|
||
@@ -485,6 +485,7 @@ __ashrdi3 (DWtype u, shift_count_type b)
|
||
#endif
|
||
|
||
#ifdef L_bswapsi2
|
||
+#if MIN_UNITS_PER_WORD > 1
|
||
SItype
|
||
__bswapsi2 (SItype u)
|
||
{
|
||
@@ -494,7 +495,9 @@ __bswapsi2 (SItype u)
|
||
| (((u) & 0x000000ff) << 24));
|
||
}
|
||
#endif
|
||
+#endif
|
||
#ifdef L_bswapdi2
|
||
+#if LONG_LONG_TYPE_SIZE > 32
|
||
DItype
|
||
__bswapdi2 (DItype u)
|
||
{
|
||
@@ -508,6 +511,7 @@ __bswapdi2 (DItype u)
|
||
| (((u) & 0x00000000000000ffull) << 56));
|
||
}
|
||
#endif
|
||
+#endif
|
||
#ifdef L_ffssi2
|
||
#undef int
|
||
int
|
||
@@ -1280,7 +1284,7 @@ __fixdfdi (DFtype a)
|
||
UDWtype
|
||
__fixunssfDI (SFtype a)
|
||
{
|
||
-#if LIBGCC2_HAS_DF_MODE
|
||
+#if LIBGCC2_HAS_DF_MODE || (FLT_MANT_DIG >= W_TYPE_SIZE)
|
||
/* Convert the SFtype to a DFtype, because that is surely not going
|
||
to lose any bits. Some day someone else can write a faster version
|
||
that avoids converting to DFtype, and verify it really works right. */
|
||
@@ -1298,7 +1302,7 @@ __fixunssfDI (SFtype a)
|
||
|
||
/* Assemble result from the two parts. */
|
||
return ((UDWtype) hi << W_TYPE_SIZE) | lo;
|
||
-#elif FLT_MANT_DIG < W_TYPE_SIZE
|
||
+#else
|
||
if (a < 1)
|
||
return 0;
|
||
if (a < Wtype_MAXp1_F)
|
||
@@ -1334,8 +1338,6 @@ __fixunssfDI (SFtype a)
|
||
return (DWtype)counter << shift;
|
||
}
|
||
return -1;
|
||
-#else
|
||
-# error
|
||
#endif
|
||
}
|
||
#endif
|
||
diff --git a/gcc/longlong.h b/gcc/longlong.h
|
||
index acb3185..20dd0ef 100644
|
||
--- a/gcc/longlong.h
|
||
+++ b/gcc/longlong.h
|
||
@@ -528,6 +528,11 @@ UDItype __umulsidi3 (USItype, USItype);
|
||
: "cbit")
|
||
#endif /* __M32R__ */
|
||
|
||
+#if defined (__m6309__) || defined (__m6809__)
|
||
+#define count_leading_zeros(COUNT,X) ((COUNT) = __builtin_clz (X))
|
||
+#define count_trailing_zeros(COUNT,X) ((COUNT) = __builtin_ctz (X))
|
||
+#endif
|
||
+
|
||
#if defined (__mc68000__) && W_TYPE_SIZE == 32
|
||
#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
|
||
__asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \
|
||
diff --git a/gcc/opth-gen.awk b/gcc/opth-gen.awk
|
||
index c3f6c04..63936c0 100644
|
||
--- a/gcc/opth-gen.awk
|
||
+++ b/gcc/opth-gen.awk
|
||
@@ -121,7 +121,7 @@ BEGIN {
|
||
END {
|
||
print "/* This file is auto-generated by opth-gen.awk. */"
|
||
print ""
|
||
-print "#ifndef OPTIONS_H"
|
||
+print "#if !defined(OPTIONS_H) && !defined(IN_LIBGCC2)"
|
||
print "#define OPTIONS_H"
|
||
print ""
|
||
print "#include \"flag-types.h\""
|
||
@@ -432,18 +432,9 @@ print ""
|
||
|
||
for (i = 0; i < n_opts; i++) {
|
||
opt = opt_args("InverseMask", flags[i])
|
||
- if (opt ~ ",") {
|
||
- vname = var_name(flags[i])
|
||
- macro = "OPTION_"
|
||
- mask = "OPTION_MASK_"
|
||
- if (vname == "") {
|
||
- vname = "target_flags"
|
||
- macro = "TARGET_"
|
||
- mask = "MASK_"
|
||
- }
|
||
- print "#define " macro nth_arg(1, opt) \
|
||
- " ((" vname " & " mask nth_arg(0, opt) ") == 0)"
|
||
- }
|
||
+ if (opt ~ ",")
|
||
+ print "#define TARGET_" nth_arg(1, opt) \
|
||
+ " ((target_flags & MASK_" nth_arg(0, opt) ") == 0)"
|
||
}
|
||
print ""
|
||
|
||
diff --git a/gcc/tree.h b/gcc/tree.h
|
||
index ece68b4..d0ec9d6 100644
|
||
--- a/gcc/tree.h
|
||
+++ b/gcc/tree.h
|
||
@@ -3563,6 +3563,8 @@ enum tree_index
|
||
TI_UINTDI_TYPE,
|
||
TI_UINTTI_TYPE,
|
||
|
||
+ TI_UINT8_TYPE,
|
||
+ TI_UINT16_TYPE,
|
||
TI_UINT32_TYPE,
|
||
TI_UINT64_TYPE,
|
||
|
||
diff --git a/gcc/version.c b/gcc/version.c
|
||
index 9744449..1a1e7e0 100644
|
||
--- a/gcc/version.c
|
||
+++ b/gcc/version.c
|
||
@@ -21,16 +21,16 @@ along with GCC; see the file COPYING3. If not see
|
||
|
||
/* This is the location of the online document giving instructions for
|
||
reporting bugs. If you distribute a modified version of GCC,
|
||
- please configure with --with-bugurl pointing to a document giving
|
||
- instructions for reporting bugs to you, not us. (You are of course
|
||
- welcome to forward us bugs reported to you, if you determine that
|
||
- they are not bugs in your modifications.) */
|
||
+ please change this to refer to a document giving instructions for
|
||
+ reporting bugs to you, not us. (You are of course welcome to
|
||
+ forward us bugs reported to you, if you determine that they are
|
||
+ not bugs in your modifications.) */
|
||
|
||
-const char bug_report_url[] = BUGURL;
|
||
+const char bug_report_url[] = "<URL:http://lost.l-w.ca/coco/lwtools/>";
|
||
|
||
/* The complete version string, assembled from several pieces.
|
||
BASEVER, DATESTAMP, DEVPHASE, and REVISION are defined by the
|
||
Makefile. */
|
||
|
||
-const char version_string[] = BASEVER DATESTAMP DEVPHASE REVISION;
|
||
+const char version_string[] = BASEVER DATESTAMP DEVPHASE REVISION " (gcc6809lw pl6)";
|
||
const char pkgversion_string[] = PKGVERSION;
|
||
diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in
|
||
index b57aeb6..940c286 100644
|
||
--- a/libgcc/Makefile.in
|
||
+++ b/libgcc/Makefile.in
|
||
@@ -374,8 +374,8 @@ endif
|
||
# Build lib2funcs. For the static library also include LIB2FUNCS_ST.
|
||
lib2funcs-o = $(patsubst %,%$(objext),$(lib2funcs) $(LIB2FUNCS_ST))
|
||
$(lib2funcs-o): %$(objext): $(gcc_srcdir)/libgcc2.c
|
||
- $(gcc_compile) -DL$* -c $(gcc_srcdir)/libgcc2.c \
|
||
- $(vis_hide)
|
||
+ ln -sf $(gcc_srcdir)/libgcc2.c $*.c && \
|
||
+ $(gcc_compile) -DL$* -c $*.c $(vis_hide) -save-temps
|
||
libgcc-objects += $(lib2funcs-o)
|
||
|
||
ifeq ($(enable_shared),yes)
|
||
@@ -410,8 +410,9 @@ endif
|
||
# Build LIB2_DIVMOD_FUNCS.
|
||
lib2-divmod-o = $(patsubst %,%$(objext),$(LIB2_DIVMOD_FUNCS))
|
||
$(lib2-divmod-o): %$(objext): $(gcc_srcdir)/libgcc2.c
|
||
- $(gcc_compile) -DL$* -c $(gcc_srcdir)/libgcc2.c \
|
||
- -fexceptions -fnon-call-exceptions $(vis_hide)
|
||
+ ln -sf $(gcc_srcdir)/libgcc2.c $*.c && \
|
||
+ $(gcc_compile) -DL$* -c $*.c \
|
||
+ -fexceptions -fnon-call-exceptions $(vis_hide) -save-temps
|
||
libgcc-objects += $(lib2-divmod-o)
|
||
|
||
ifeq ($(enable_shared),yes)
|
||
@@ -443,7 +444,8 @@ endif
|
||
ifneq ($(FPBIT),)
|
||
fpbit-o = $(patsubst %,%$(objext),$(FPBIT_FUNCS))
|
||
$(fpbit-o): %$(objext): $(FPBIT)
|
||
- $(gcc_compile) -DFINE_GRAINED_LIBRARIES -DL$* -c $(FPBIT) $(vis_hide)
|
||
+ ln -sf $(FPBIT) $*.c && \
|
||
+ $(gcc_compile) -DFINE_GRAINED_LIBRARIES -DL$* -c $*.c $(vis_hide) -save-temps
|
||
libgcc-objects += $(fpbit-o)
|
||
|
||
ifeq ($(enable_shared),yes)
|
||
@@ -458,7 +460,8 @@ endif
|
||
ifneq ($(DPBIT),)
|
||
dpbit-o = $(patsubst %,%$(objext),$(DPBIT_FUNCS))
|
||
$(dpbit-o): %$(objext): $(DPBIT)
|
||
- $(gcc_compile) -DFINE_GRAINED_LIBRARIES -DL$* -c $(DPBIT) $(vis_hide)
|
||
+ ln -sf $(DPBIT) $*.c && \
|
||
+ $(gcc_compile) -DFINE_GRAINED_LIBRARIES -DL$* -c $*.c $(vis_hide) -save-temps
|
||
libgcc-objects += $(dpbit-o)
|
||
|
||
ifeq ($(enable_shared),yes)
|
||
diff --git a/libgcc/config.host b/libgcc/config.host
|
||
index 25e949e..7154892 100644
|
||
--- a/libgcc/config.host
|
||
+++ b/libgcc/config.host
|
||
@@ -371,6 +371,8 @@ m32r-*-linux*)
|
||
;;
|
||
m32rle-*-linux*)
|
||
;;
|
||
+m6809*)
|
||
+ ;;
|
||
m68hc11-*-*|m6811-*-*)
|
||
;;
|
||
m68hc12-*-*|m6812-*-*)
|
||
diff --git a/libgcc/fixed-obj.mk b/libgcc/fixed-obj.mk
|
||
index 3c7c2f3..eb3aa3a 100644
|
||
--- a/libgcc/fixed-obj.mk
|
||
+++ b/libgcc/fixed-obj.mk
|
||
@@ -23,7 +23,7 @@ endif
|
||
#$(info $o$(objext): -DL$($o-label) $($o-opt))
|
||
|
||
$o$(objext): %$(objext): $(gcc_srcdir)/config/fixed-bit.c
|
||
- $(gcc_compile) -DL$($*-label) $($*-opt) -c $(gcc_srcdir)/config/fixed-bit.c $(vis_hide)
|
||
+ $(gcc_compile) -DL$($*-label) $($*-opt) -c $(gcc_srcdir)/config/fixed-bit.c $(vis_hide) -save-temps
|
||
|
||
ifeq ($(enable_shared),yes)
|
||
$(o)_s$(objext): %_s$(objext): $(gcc_srcdir)/config/fixed-bit.c
|
||
--
|
||
2.7.0
|
||
|
||
|
||
From 696169dbc7af549e249c5122554a1ced664bb980 Mon Sep 17 00:00:00 2001
|
||
From: Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||
Date: Sun, 15 Jan 2017 21:34:56 +0100
|
||
Subject: Fix build with gcc5.
|
||
|
||
From
|
||
https://github.com/DragonFlyBSD/DPorts/commit/a680cc6ef758e2f15be8bf8209da51658e02d710
|
||
|
||
diff --git a/gcc/cp/cfns.h b/gcc/cp/cfns.h
|
||
index 62cdfab..6bbc8c9 100644
|
||
--- a/gcc/cp/cfns.h
|
||
+++ b/gcc/cp/cfns.h
|
||
@@ -53,6 +53,9 @@ __inline
|
||
static unsigned int hash (const char *, unsigned int);
|
||
#ifdef __GNUC__
|
||
__inline
|
||
+#ifdef __GNUC_STDC_INLINE__
|
||
+__attribute__ ((__gnu_inline__))
|
||
+#endif
|
||
#endif
|
||
const char * libc_name_p (const char *, unsigned int);
|
||
/* maximum key range = 391, duplicates = 0 */
|
||
@@ -96,7 +99,7 @@ hash (register const char *str, register unsigned int len)
|
||
400, 400, 400, 400, 400, 400, 400, 400, 400, 400,
|
||
400, 400, 400, 400, 400, 400, 400
|
||
};
|
||
- register int hval = len;
|
||
+ register int hval = (int) len;
|
||
|
||
switch (hval)
|
||
{
|
||
--
|
||
2.7.0
|
||
|