Mercurial > hg > index.cgi
view extra/gcc6809lw-4.6.1-1.patch @ 235:e3741cf53e00
Fix error messages related to undefined symbols in lwlink
Make lwlink not complain about seciton base and length symbols. Also silence
duplicate complaints about undefined symbols. There is no need to complain
about undefined symbols during the file/section resolution stage! If they
are truly undefined, they'll still be undefined at the reference resolution
stage.
author | William Astle <lost@l-w.ca> |
---|---|
date | Sat, 11 Aug 2012 15:18:58 -0600 |
parents | e0cc66fd0551 |
children |
line wrap: on
line source
diff -urN gcc-4.6.1-orig/config.sub gcc-4.6.1/config.sub --- gcc-4.6.1-orig/config.sub 2010-05-25 07:22:07.000000000 -0600 +++ gcc-4.6.1/config.sub 2011-09-17 14:07:51.597643628 -0600 @@ -313,7 +313,7 @@ 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 @@ | 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 @@ basic_machine=arm-unknown os=-cegcc ;; + coco) + basic_machine=coco + os=-none + ;; convex-c1) basic_machine=c1-convex os=-bsd diff -urN gcc-4.6.1-orig/configure gcc-4.6.1/configure --- gcc-4.6.1-orig/configure 2011-03-16 12:27:36.000000000 -0600 +++ gcc-4.6.1/configure 2011-09-17 14:06:01.187643616 -0600 @@ -3441,6 +3441,9 @@ m32r-*-*) noconfigdirs="$noconfigdirs ${libgcj}" ;; + m6809*) + noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}" + ;; m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*) noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 ${libgcj}" libgloss_dir=m68hc11 diff -urN gcc-4.6.1-orig/configure.ac gcc-4.6.1/configure.ac --- gcc-4.6.1-orig/configure.ac 2011-03-16 12:27:36.000000000 -0600 +++ gcc-4.6.1/configure.ac 2011-09-17 14:06:01.187643616 -0600 @@ -887,6 +887,9 @@ m32r-*-*) noconfigdirs="$noconfigdirs ${libgcj}" ;; + m6809*) + noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}" + ;; m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*) noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 ${libgcj}" libgloss_dir=m68hc11 diff -urN gcc-4.6.1-orig/gcc/calls.c gcc-4.6.1/gcc/calls.c --- gcc-4.6.1-orig/gcc/calls.c 2011-06-06 05:46:14.000000000 -0600 +++ gcc-4.6.1/gcc/calls.c 2011-09-17 14:06:01.217643616 -0600 @@ -2434,7 +2434,7 @@ 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 -urN gcc-4.6.1-orig/gcc/config/m6809/crt0.S gcc-4.6.1/gcc/config/m6809/crt0.S --- gcc-4.6.1-orig/gcc/config/m6809/crt0.S 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/crt0.S 2011-09-17 14:06:01.227643616 -0600 @@ -0,0 +1,180 @@ +;;; +;;; 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 + + + /* Declare all linker sections, and combine them into a single bank */ + .bank prog + .area .text (BANK=prog) + .area .data (BANK=prog) + .area .ctors (BANK=prog) + .word 0 + .area .dtors (BANK=prog) + .word 0 + .area .bss (BANK=prog) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;; + ;;; __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 + + .end __start diff -urN gcc-4.6.1-orig/gcc/config/m6809/libgcc1.s gcc-4.6.1/gcc/config/m6809/libgcc1.s --- gcc-4.6.1-orig/gcc/config/m6809/libgcc1.s 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/libgcc1.s 2011-09-17 14:06:01.227643616 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/m6809.c gcc-4.6.1/gcc/config/m6809/m6809.c --- gcc-4.6.1-orig/gcc/config/m6809/m6809.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/m6809.c 2011-09-18 19:48:42.137654855 -0600 @@ -0,0 +1,3013 @@ +/*------------------------------------------------------------------- + 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)) + { + case CONST_INT: + /* Constants that can fit into 1 byte should be + * loaded into a Q_REGS reg */ + 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 (((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 -urN gcc-4.6.1-orig/gcc/config/m6809/m6809.h gcc-4.6.1/gcc/config/m6809/m6809.h --- gcc-4.6.1-orig/gcc/config/m6809/m6809.h 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/m6809.h 2011-09-18 19:47:50.207654849 -0600 @@ -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 | SOFT_M_REGBITS) + +/* 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 -urN gcc-4.6.1-orig/gcc/config/m6809/m6809.md gcc-4.6.1/gcc/config/m6809/m6809.md --- gcc-4.6.1-orig/gcc/config/m6809/m6809.md 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/m6809.md 2011-09-21 20:40:01.287068005 -0600 @@ -0,0 +1,2359 @@ +;; 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,tmu") + (match_operand:HI 1 "general_operand" " a,a,d,tmiu,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,tm,q,tm,q,z") + (match_operand:QI 1 "general_operand" " q,O,O,tmi,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_operand:HI 1 "memory_operand" "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_operand:HI 1 "memory_operand" "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" "tmi,tmi")] 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,tm,tm,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,tmi")))] + "" + "@ + 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, q") + (minus:QI (match_operand:QI 1 "whole_register_operand" "0, 0, I, tmn, 0") + (match_operand:QI 2 "whole_general_operand" "I, mi, 0, 0, t")))] + "" + "@ + dec%0 + sub%0\t%2 + dec%0\;neg%0 + sub%0\t%1\;neg%0 + sub%0\t%2" + [(set_attr "length" "1,3,2,4,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,tm,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,tm,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,tm,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,tm") + (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 -urN gcc-4.6.1-orig/gcc/config/m6809/m6809.opt gcc-4.6.1/gcc/config/m6809/m6809.opt --- gcc-4.6.1-orig/gcc/config/m6809/m6809.opt 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/m6809.opt 2011-09-17 14:06:01.227643616 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/m6809-protos.h gcc-4.6.1/gcc/config/m6809/m6809-protos.h --- gcc-4.6.1-orig/gcc/config/m6809/m6809-protos.h 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/m6809-protos.h 2011-09-17 17:26:19.227644879 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/predicates.md gcc-4.6.1/gcc/config/m6809/predicates.md --- gcc-4.6.1-orig/gcc/config/m6809/predicates.md 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/predicates.md 2011-09-18 15:09:37.057653095 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/t-coco gcc-4.6.1/gcc/config/m6809/t-coco --- gcc-4.6.1-orig/gcc/config/m6809/t-coco 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/t-coco 2011-09-17 14:06:01.227643616 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/t-m6809 gcc-4.6.1/gcc/config/m6809/t-m6809 --- gcc-4.6.1-orig/gcc/config/m6809/t-m6809 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/t-m6809 2011-09-17 21:38:35.437646470 -0600 @@ -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 -urN gcc-4.6.1-orig/gcc/config/m6809/t-sim gcc-4.6.1/gcc/config/m6809/t-sim --- gcc-4.6.1-orig/gcc/config/m6809/t-sim 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/gcc/config/m6809/t-sim 2011-09-17 14:06:01.227643616 -0600 @@ -0,0 +1 @@ +CRT0STUFF_T_CFLAGS += -DTARGET_SIM diff -urN gcc-4.6.1-orig/gcc/config.gcc gcc-4.6.1/gcc/config.gcc --- gcc-4.6.1-orig/gcc/config.gcc 2011-05-22 14:03:43.000000000 -0600 +++ gcc-4.6.1/gcc/config.gcc 2011-09-17 14:08:56.257643636 -0600 @@ -374,6 +374,9 @@ cpu_type=m32r extra_options="${extra_options} g.opt" ;; +m6809-*-*) + cpu_type=m6809 + ;; m68k-*-*) extra_headers=math-68881.h ;; @@ -1689,6 +1692,12 @@ 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 -urN gcc-4.6.1-orig/gcc/gcse.c gcc-4.6.1/gcc/gcse.c --- gcc-4.6.1-orig/gcc/gcse.c 2011-02-02 23:04:04.000000000 -0700 +++ gcc-4.6.1/gcc/gcse.c 2011-09-18 17:25:17.527653952 -0600 @@ -833,7 +833,6 @@ max_distance = (GCSE_COST_DISTANCE_RATIO * cost) / 10; if (max_distance == 0) return 0; - gcc_assert (max_distance > 0); } else diff -urN gcc-4.6.1-orig/gcc/libgcc2.c gcc-4.6.1/gcc/libgcc2.c --- gcc-4.6.1-orig/gcc/libgcc2.c 2011-01-03 13:52:22.000000000 -0700 +++ gcc-4.6.1/gcc/libgcc2.c 2011-09-17 14:06:01.227643616 -0600 @@ -485,6 +485,7 @@ #endif #ifdef L_bswapsi2 +#if MIN_UNITS_PER_WORD > 1 SItype __bswapsi2 (SItype u) { @@ -494,7 +495,9 @@ | (((u) & 0x000000ff) << 24)); } #endif +#endif #ifdef L_bswapdi2 +#if LONG_LONG_TYPE_SIZE > 32 DItype __bswapdi2 (DItype u) { @@ -508,6 +511,7 @@ | (((u) & 0x00000000000000ffull) << 56)); } #endif +#endif #ifdef L_ffssi2 #undef int int @@ -1280,7 +1284,7 @@ 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 @@ /* 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 @@ return (DWtype)counter << shift; } return -1; -#else -# error #endif } #endif diff -urN gcc-4.6.1-orig/gcc/longlong.h gcc-4.6.1/gcc/longlong.h --- gcc-4.6.1-orig/gcc/longlong.h 2011-06-06 08:34:54.000000000 -0600 +++ gcc-4.6.1/gcc/longlong.h 2011-09-17 14:06:01.227643616 -0600 @@ -527,6 +527,11 @@ : "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 -urN gcc-4.6.1-orig/gcc/Makefile.in gcc-4.6.1/gcc/Makefile.in --- gcc-4.6.1-orig/gcc/Makefile.in 2011-05-23 12:12:34.000000000 -0600 +++ gcc-4.6.1/gcc/Makefile.in 2011-09-17 14:06:01.197643616 -0600 @@ -1987,14 +1987,14 @@ # 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 -urN gcc-4.6.1-orig/gcc/opth-gen.awk gcc-4.6.1/gcc/opth-gen.awk --- gcc-4.6.1-orig/gcc/opth-gen.awk 2011-02-08 10:41:00.000000000 -0700 +++ gcc-4.6.1/gcc/opth-gen.awk 2011-09-17 14:06:01.227643616 -0600 @@ -121,7 +121,7 @@ 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 @@ 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 -urN gcc-4.6.1-orig/gcc/tree.h gcc-4.6.1/gcc/tree.h --- gcc-4.6.1-orig/gcc/tree.h 2011-06-14 09:28:21.000000000 -0600 +++ gcc-4.6.1/gcc/tree.h 2011-09-17 20:28:05.987646026 -0600 @@ -3563,6 +3563,8 @@ TI_UINTDI_TYPE, TI_UINTTI_TYPE, + TI_UINT8_TYPE, + TI_UINT16_TYPE, TI_UINT32_TYPE, TI_UINT64_TYPE, diff -urN gcc-4.6.1-orig/gcc/version.c gcc-4.6.1/gcc/version.c --- gcc-4.6.1-orig/gcc/version.c 2009-04-21 13:03:23.000000000 -0600 +++ gcc-4.6.1/gcc/version.c 2011-09-18 19:49:48.437654863 -0600 @@ -21,16 +21,16 @@ /* 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)"; const char pkgversion_string[] = PKGVERSION; diff -urN gcc-4.6.1-orig/libgcc/config.host gcc-4.6.1/libgcc/config.host --- gcc-4.6.1-orig/libgcc/config.host 2011-03-14 00:06:23.000000000 -0600 +++ gcc-4.6.1/libgcc/config.host 2011-09-17 14:06:01.257643616 -0600 @@ -380,6 +380,8 @@ ;; m32rle-*-linux*) ;; +m6809*) + ;; m68hc11-*-*|m6811-*-*) ;; m68hc12-*-*|m6812-*-*) diff -urN gcc-4.6.1-orig/libgcc/fixed-obj.mk gcc-4.6.1/libgcc/fixed-obj.mk --- gcc-4.6.1-orig/libgcc/fixed-obj.mk 2007-09-17 16:18:13.000000000 -0600 +++ gcc-4.6.1/libgcc/fixed-obj.mk 2011-09-17 14:06:01.257643616 -0600 @@ -23,7 +23,7 @@ #$(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 diff -urN gcc-4.6.1-orig/libgcc/Makefile.in gcc-4.6.1/libgcc/Makefile.in --- gcc-4.6.1-orig/libgcc/Makefile.in 2011-01-25 21:19:58.000000000 -0700 +++ gcc-4.6.1/libgcc/Makefile.in 2011-09-17 14:06:01.257643616 -0600 @@ -374,8 +374,8 @@ # 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 @@ # 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 @@ 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 @@ 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 -urN gcc-4.6.1-orig/README.LW gcc-4.6.1/README.LW --- gcc-4.6.1-orig/README.LW 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.1/README.LW 2011-09-18 19:44:52.457654831 -0600 @@ -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!