Mercurial > hg > index.cgi
view extra/gcc6809lw-4.6.4-8.patch @ 582:1ede8f8621cf default tip
Actually treat "0b" as 0 since it matches the "b" suffix for binary numbers
author | William Astle <lost@l-w.ca> |
---|---|
date | Thu, 08 Aug 2024 13:46:19 -0600 |
parents | 818b096ac128 |
children |
line wrap: on
line source
diff -urN gcc-4.6.4-clean/config.sub gcc-4.6.4/config.sub --- gcc-4.6.4-clean/config.sub 2010-05-25 07:22:07.000000000 -0600 +++ gcc-4.6.4/config.sub 2017-11-28 21:12:11.136911706 -0700 @@ -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.4-clean/configure gcc-4.6.4/configure --- gcc-4.6.4-clean/configure 2011-12-18 03:03:44.000000000 -0700 +++ gcc-4.6.4/configure 2017-11-28 21:12:11.136911706 -0700 @@ -3439,6 +3439,9 @@ m32r-*-*) noconfigdirs="$noconfigdirs ${libgcj}" ;; + m6809*) + noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}" + ;; m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*) noconfigdirs="$noconfigdirs target-libstdc++-v3 ${libgcj}" libgloss_dir=m68hc11 diff -urN gcc-4.6.4-clean/configure.ac gcc-4.6.4/configure.ac --- gcc-4.6.4-clean/configure.ac 2011-11-18 04:45:44.000000000 -0700 +++ gcc-4.6.4/configure.ac 2017-11-28 21:12:11.140911685 -0700 @@ -885,6 +885,9 @@ m32r-*-*) noconfigdirs="$noconfigdirs ${libgcj}" ;; + m6809*) + noconfigdirs="$noconfigdirs target-libiberty target-libstdc++-v3 target-libgloss ${libgcj}" + ;; m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*) noconfigdirs="$noconfigdirs target-libstdc++-v3 ${libgcj}" libgloss_dir=m68hc11 diff -urN gcc-4.6.4-clean/gcc/calls.c gcc-4.6.4/gcc/calls.c --- gcc-4.6.4-clean/gcc/calls.c 2012-02-09 10:27:25.000000000 -0700 +++ gcc-4.6.4/gcc/calls.c 2017-11-28 21:12:11.140911685 -0700 @@ -2561,7 +2561,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.4-clean/gcc/config/m6809/crt0.S gcc-4.6.4/gcc/config/m6809/crt0.S --- gcc-4.6.4-clean/gcc/config/m6809/crt0.S 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/crt0.S 2017-11-28 21:12:11.152911619 -0700 @@ -0,0 +1,173 @@ +;;; +;;; Copyright 2006, 2007, 2008, 2009 by Brian Dominy <brian@oddchange.com> +;;; +;;; This file is part of GCC. +;;; +;;; GCC is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3, or (at your option) +;;; any later version. +;;; +;;; GCC is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. + +;;; You should have received a copy of the GNU General Public License +;;; along with GCC; see the file COPYING3. If not see +;;; <http://www.gnu.org/licenses/>. + + /* Declare external for main() */ + .globl _main + + +/* The startup is heavily dependent on the type of machine and +OS environment that is available at the start point. +For the most part, the general idea is the same across machines, +but the implementation is vastly different. This is managed via +conditional compiles throughout the startup code for each of the +supported machines. */ + +#ifdef TARGET_COCO /* CoCo memory map */ + +#define COCO_RAMROM_MODE 0xFFDE +#define COCO_ALLRAM_MODE 0xFFDF +#define COCO_PAGE1 0xFFD5 + +/* SAM M1 and M0 adjusts the memory size */ + +#define BASIC_WARMSTART_FLAG 0x0071 +#define BASIC_START 0xA027 + +#define __STACK_TOP 0x6800 + +#else /* Simulator (default) memory map */ + +#define SIM_EXIT_REG 0xFF01 + +#define __STACK_TOP 0xFE00 + +#endif + + + .area .data + .area .ctors + .area .dtors + .area .bss + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;; + ;;; __exit : Exit point from the program + ;;; For simulation, this writes to a special I/O register that + ;;; the simulator interprets as end-of-program. + ;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .area .text + .globl __exit +__exit: +#ifdef TARGET_COCO + ;; Go back to ROM/RAM mode + sta COCO_RAMROM_MODE + clr BASIC_WARMSTART_FLAG + jmp BASIC_START +#else + tfr x,d + stb SIM_EXIT_REG + bra __exit +#endif + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;; + ;;; __start : Entry point to the program + ;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + .area .text + .globl __start +__start: + +#ifdef HAVE_DIRECT + ;; Initialize the direct page pointer + lda #<s_.direct + tfr a,dp +#endif + +#ifdef TARGET_COCO + ;; Turn off interrupts + orcc #(0x10|0x40) + + ;; Setup All RAM Mode + sta COCO_ALLRAM_MODE +#endif /* TARGET_COCO */ + + ;; Initialize the stack + lds #__STACK_TOP - 2 + + ;; Call any "initializer" functions + ldu #s_.ctors +__ctors_loop: + ldy ,u++ + cmpy #0 + beq __ctors_done + jsr ,y + bra __ctors_loop +__ctors_done: + + ;; Enable interrupts on the simulator +#ifndef TARGET_COCO + andcc #~(0x10|0x40) +#endif + + ;; Set up the environment + + ;; Set up argc/argv arrays + + ;; Call the main function. The exit code will + ;; be returned in the X register, unless compiled + ;; with -mdret, in which case it comes back in D. + jsr _main + + ;; Call any finalizer functions + ldu #s_.dtors +__dtors_loop: + ldy ,u++ + cmpy #0 + beq __dtors_done + jsr ,y + bra __dtors_loop +__dtors_done: + + ;; If main returns, then invoke _exit() to stop the program + ;; The C library doesn't support -mdret yet, so move the + ;; argument first. +#ifdef __DRET__ + tfr d,x +#endif + jmp _exit + + + + ;;; + ;;; __crt0_vector : Default handler for interrupts + ;;; + .area .text +___crt0_vector: + ;; The default behavior is to simply ignore all + ;; non-reset interrupts. + rti + + + ;;; + ;;; vector : The interrupt vector table + ;;; The linker will ensure that this gets loaded at address 0xFFF0. + ;;; + .area vector +vectors: + .word ___crt0_vector + .word ___crt0_vector + .word ___crt0_vector + .word ___crt0_vector + .word ___crt0_vector + .word ___crt0_vector + .word ___crt0_vector + .word __start diff -urN gcc-4.6.4-clean/gcc/config/m6809/libgcc1.s gcc-4.6.4/gcc/config/m6809/libgcc1.s --- gcc-4.6.4-clean/gcc/config/m6809/libgcc1.s 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/libgcc1.s 2017-11-28 21:12:11.152911619 -0700 @@ -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.4-clean/gcc/config/m6809/m6809.c gcc-4.6.4/gcc/config/m6809/m6809.c --- gcc-4.6.4-clean/gcc/config/m6809/m6809.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/m6809.c 2017-11-28 21:12:11.152911619 -0700 @@ -0,0 +1,3025 @@ +/*------------------------------------------------------------------- + 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); + cc_status.flags |= CC_NO_OVERFLOW; + 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); + cc_status.flags |= CC_NO_OVERFLOW; + return; + } + + /* Function calls clobber the cc's. */ + else if (GET_CODE (SET_SRC (exp)) == CALL) { + CC_STATUS_INIT; + return; + } + + /* Tests and compares set the cc's in predictable ways. */ + else if (SET_DEST (exp) == cc0_rtx) + { + cc_status.flags = 0; + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = SET_DEST (exp); + return; + } + + else if (A_REG_P (SET_DEST (exp))) + { + CC_STATUS_INIT; + return; + } + + else + { + /* Certain instructions affect the condition codes. */ + switch (src_code) + { + case PLUS: + case MINUS: + case NEG: + case ASHIFT: + /* These instructions set the condition codes, + * and may modify the V bit. */ + cc_status.flags |= CC_NO_OVERFLOW; + /* FALLTHRU */ + + case AND: + case IOR: + case XOR: + case ASHIFTRT: + case LSHIFTRT: + /* These instructions set the condition codes, + * but cannot overflow (V=0). */ + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = SET_DEST (exp); + break; + + default: + /* Everything else is clobbered */ + CC_STATUS_INIT; + } + return; + } + } /* SET */ + + else if (GET_CODE (exp) == PARALLEL + && GET_CODE (XVECEXP (exp, 0, 0)) == SET) + { + if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx) + return; + if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx) + { + CC_STATUS_INIT; + cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0)); + return; + } + } + + /*** default action if we haven't recognized something + and returned earlier ***/ + CC_STATUS_INIT; +} + + +/** Returns nonzero if the expression EXP can be implemented using one + * of the 6809's single operand instructions. */ +int +m6809_single_operand_operator (rtx exp) +{ + rtx op1; + HOST_WIDE_INT val; + enum rtx_code code; + + debug_rtx(exp); + + code = GET_CODE (exp); + + /* Unary operators always qualify */ + switch (code) + { + case NEG: + case NOT: + return 1; + + default: + break; + } + + /* Binary operators can only qualify if the second + * argument is a CONST_INT of certain value. */ + op1 = XEXP (exp, 1); + if (GET_CODE (op1) != CONST_INT) + return 0; + val = INTVAL (op1); + switch (code) + { + case PLUS: + case MINUS: + if (val == -1 || val == 1) + return 1; + break; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + case ROTATE: + case ROTATERT: + if (val == 1) + return 1; + break; + + default: + break; + } + + return 0; +} + + +/** Return a bitarray of the hard registers which are used by a function. */ +unsigned int +m6809_get_live_regs (void) +{ + unsigned int regs = 0; + int regno; + + if (frame_pointer_needed) + regs |= (1 << HARD_FRAME_POINTER_REGNUM); + + for (regno = HARD_X_REGNUM; regno <= HARD_U_REGNUM; regno++) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) + regs |= (1 << regno); + + return regs; +} + + +/** Return a printable version of a list of hard registers, suitable + * for use in a PSHx or PULx insn. */ +const char * +m6809_get_regs_printable (unsigned int regs) +{ + static char list[64]; + char *listp = list; + unsigned int regno; + + for (regno=0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((regs & (1 << regno)) && !S_REGNO_P (regno)) + listp += sprintf (listp, + (listp == list) ? "%s" : ",%s", reg_names[regno]); + + return list; +} + + +/** Return the total number of bytes covered by a set of hard registers. */ +unsigned int +m6809_get_regs_size (unsigned int regs) +{ + unsigned int regno; + unsigned int size = 0; + + for (regno=0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + /* Only count register in the given register set */ + if (REGSET_CONTAINS_P (regno, regs)) + { + /* Add 1 or 2 byte, depending on the size of the register. + * Since 'D' may be in both sets, check for WORD_REGSET first. */ + if (REGSET_CONTAINS_P(regno, WORD_REGSET)) + size += 2; + else if (REGSET_CONTAINS_P(regno, BYTE_REGSET)) + size++; + } + } + return size; +} + + +/* Given the target of call instruction in X, + * return the tree node that contains the function declaration for + * that target. + * + * If the rtx or the tree do not appear valid for any reason, + * then return NULL_TREE. + */ +static tree call_target_decl (rtx x) +{ + tree decl; + + /* Make sure the target is really a MEM. */ + if (!x || !MEM_P (x)) + return NULL_TREE; + + /* Make sure the address is a SYMBOL_REF. */ + x = XEXP (x, 0); + if (!x || (GET_CODE (x) != SYMBOL_REF)) + return NULL_TREE; + + /* Get the declaration of this symbol */ + decl = SYMBOL_REF_DECL (x); + + /* Make sure the declaration is really a function. */ + if (!decl || (TREE_CODE(decl) != FUNCTION_DECL)) + return NULL_TREE; + + return decl; +} + + +/** Returns nonzero if a function, whose declaration is in DECL, + * was declared to have the attribute given by ATTR_NAME. */ +int +m6809_function_has_type_attr_p (tree decl, const char *attr_name) +{ + tree type; + + type = TREE_TYPE (decl); + return lookup_attribute (attr_name, TYPE_ATTRIBUTES (type)) != NULL; +} + + + +/** Returns nonzero if the current function was declared to have the + * attribute given by ATTR_NAME. */ +int +m6809_current_function_has_type_attr_p (const char *attr_name) +{ + return m6809_function_has_type_attr_p (current_function_decl, attr_name); +} + + +/** Return nonzero if the current function has no return value. */ +int +m6809_current_function_is_void (void) +{ + return (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))); +} + + +/** Get the value of a declaration's 'bank', as set by the 'bank' + * attribute. If no bank was declared, it returns NULL by default. */ +const char * +m6809_get_decl_bank (tree decl) +{ + tree attr; + + /* Lookup the 'bank' attribute. If it does not exist, then + * return NULL */ + attr = lookup_attribute ("bank", DECL_ATTRIBUTES (decl)); + if (attr == NULL_TREE) + return NULL; + + /* Make sure it has a value assigned to it */ + attr = TREE_VALUE (attr); + if (attr == NULL_TREE) + { + warning (WARNING_OPT "banked function did not declare a bank number"); + return NULL; + } + + /* Return the bank name */ + attr = TREE_VALUE (attr); + return TREE_STRING_POINTER (attr); +} + + +void +m6809_declare_function_name (FILE *asm_out_file, const char *name, tree decl) +{ + /* Check the function declaration for special properties. + * + * If the function was declare with __attribute__((bank)), output + * assembler definitions to force the function to go into the named + * bank. + */ + const char *bank_name = m6809_get_decl_bank (decl); + if (bank_name != NULL) + { + /* Declare __self_bank as a local assembler value that denotes + * which bank the current function is in. This is required only + * when the bank actually changes. */ + if (strcmp (bank_name, current_bank_name)) + { + fprintf (asm_out_file, "__self_bank\t.equ %s\n", bank_name); + strcpy (current_bank_name, bank_name); + } + + /* Declare a global assembler value that denotes which bank the + * named function is in. */ + fprintf (asm_out_file, "__%s_bank\t.gblequ %s\n", name, bank_name); + + /* Force the current function into a new area */ + fprintf (asm_out_file, "\t.bank bank_%s (FSFX=_%s)\n", + bank_name, bank_name); + fprintf (asm_out_file, "\t.area bank_%s (BANK=bank_%s)\n", + bank_name, bank_name); + } + + /* Emit the label for the function's name */ + ASM_OUTPUT_LABEL (asm_out_file, name); +} + +#if 0 +/** + * Handle pragmas. Note that only the last branch pragma seen in the + * source has any affect on code generation. + */ + +#define BAD_PRAGMA(msgid, arg) \ + do { warning (WARNING_OPT msgid, arg); return -1; } while (0) + +static int +pragma_parse (const char *name, tree *sect) +{ + tree s, x; + + if (pragma_lex (&x) != CPP_OPEN_PAREN) + BAD_PRAGMA ("missing '(' after '#pragma %s' - ignored", name); + + if (pragma_lex (&s) != CPP_STRING) + BAD_PRAGMA ("missing section name in '#pragma %s' - ignored", name); + + if (pragma_lex (&x) != CPP_CLOSE_PAREN) + BAD_PRAGMA ("missing ')' for '#pragma %s' - ignored", name); + + if (pragma_lex (&x) != CPP_EOF) + warning (WARNING_OPT "junk at end of '#pragma %s'", name); + + *sect = s; + return 0; +} + + +/* + * Handle #pragma section. + * This is deprecated; code should use __attribute__(section("name")) + * instead. + */ +void pragma_section (cpp_reader *pfile ATTRIBUTE_UNUSED) +{ + tree sect; + + if (pragma_parse ("section", §)) + return; + + snprintf (code_section_op, 6+TREE_STRING_LENGTH (sect), + ".area\t%s", TREE_STRING_POINTER (sect)); + snprintf (data_section_op, 6+TREE_STRING_LENGTH (sect), + ".area\t%s", TREE_STRING_POINTER (sect)); + + /* Mark a flag that sections have changed. Upon emitting another + * declaration, the new .area directive will be written. */ + section_changed++; +} +#endif + +/** + * Check a `double' value for validity for a particular machine mode. + * Called by the CHECK_FLOAT_VALUE() machine-dependent macro. + */ +int +check_float_value (enum machine_mode mode, double *d, int overflow) +{ + if (mode == SFmode) { + if (*d > 1.7014117331926443e+38) { + error("magnitude of constant too large for `float'"); + *d = 1.7014117331926443e+38; + } + else if (*d < -1.7014117331926443e+38) { + error("magnitude of constant too large for `float'"); + *d = -1.7014117331926443e+38; + } + else if ((*d > 0) && (*d < 2.9387358770557188e-39)) { + warning(WARNING_OPT "`float' constant truncated to zero"); + *d = 0.0; + } + else if ((*d < 0) && (*d > -2.9387358770557188e-39)) { + warning(WARNING_OPT "`float' constant truncated to zero"); + *d = 0.0; + } + } + return overflow; +} + + + +/** Declare that the target supports named output sections. */ +bool m6809_have_named_section = (bool)1; + + +/** Write to the assembler file a directive to place + * subsequent objects to a different section in the + * object file. ASxxxx uses the "area" directive for + * this purpose. It does not however support generalized + * alignment, and can only place items on an odd/even + * boundary. */ +void +m6809_asm_named_section ( + const char *name, + unsigned int flags ATTRIBUTE_UNUSED, + tree decl) +{ + fprintf (asm_out_file, "\t.area\t%s\n", name); +} + + +enum reg_class +m6809_preferred_reload_class (rtx x, enum reg_class regclass) +{ + /* Check cases based on type code of rtx */ + switch (GET_CODE(x)) + { + /* + * Observation, 2015-07-19, William Astle + * + * The original comparison for range for 16 bits was wrong, adding 0x80 + * instead of 0x8000. Replaced both 8 bit and 16 bit comparisions with + * a more straight forward range comparison - excessive cleverness isn't + * really required here. + */ + case CONST_INT: + /* Constants that can fit into 1 byte should be + * loaded into a Q_REGS reg */ + if ((INTVAL(x) >= -128 && INTVAL(x) <= 127) && +// if (((unsigned) (INTVAL(x) + 0x80) < 0x100) && + (regclass > A_REGS)) + return Q_REGS; + + /* 16-bit constants should be loaded into A_REGS + * when possible. gcc may already require A_REGS + * or D_REGS for certain types of instructions. + * This case applies mostly to simple copy operations + * to/from memory when any register will do, but + * it's best to avoid using D register since it is + * needed for other things. + */ + else if ((INTVAL(x) >= -32768 && INTVAL(x) <= 32767) && +// else if (((unsigned) (INTVAL(x) + 0x80) < 0x10000) && + (regclass > A_REGS)) + return A_REGS; + break; + + case SYMBOL_REF: + case LABEL_REF: + /* Addresses should always be loaded into A_REGS */ + if (regclass >= A_REGS) + return (A_REGS); + + default: + break; + } + + /* Check cases based on mode of rtx */ + if ((GET_MODE(x) == QImode) && (regclass != A_REGS)) + return Q_REGS; + + /* Default: return whatever class reload suggested */ + return regclass; +} + + +/** + * Check a new declaration for the "section" attribute. + * If it exists, and the target section is "direct", then mark + * the declaration (in RTL) to indicate special treatment. + * When the variable is referenced later, we test for this flag + * and can emit special asm text to force the assembler to use + * short instructions. + */ +static void +m6809_encode_section_info (tree decl, rtx rtl, int new_decl_p ATTRIBUTE_UNUSED) +{ + tree attr, id; + const char *name; + const char *decl_name; + + /* We only care about variable declarations, not functions */ + if (TREE_CODE (decl) != VAR_DECL) + return; + + /* For debugging purposes only; grab the decl's name */ + decl_name = IDENTIFIER_POINTER (DECL_NAME (decl)); + + /* Give up if the decl doesn't have any RTL */ + if (!DECL_RTL (decl)) + return; + + /* See if it has a section attribute */ + attr = lookup_attribute ("section", DECL_ATTRIBUTES (decl)); + if (!attr) + return; + + /* See if the section attribute has a value */ + id = TREE_VALUE (TREE_VALUE (attr)); + if (!id) + return; + name = TREE_STRING_POINTER (id); + if (!name) + return; + + /* See if the value is 'direct'. If so, mark it. */ + if (!strcmp (name, "direct")) + SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; +} + + +/** + * Output code to perform a complex shift, for which there is no + * direct support in the instruction set. + * + * shift1 is an instruction pattern for performing a 1-bit modification. + * This code wraps that pattern in a loop to perform the shift N times, + * where N is given by the address register in operands[2]. + * + * To support 16-bit shifts, shift2 can also be provided: it is + * a second instruction to be included in the loop. 8-bit shift + * insns will pass NULL here. + * + * The insn length of shift1/shift2 is assumed to be 1 byte, + * which works in all of the cases it is needed so far. + */ +static void +m6809_gen_register_shift ( + rtx *operands, + const char *shift1, + const char *shift2 ) +{ + char beq_pattern[32]; + char bra_pattern[32]; + + int shiftlen = (shift1 && shift2) ? 2 : 1; + int cmplen = (REGNO (operands[2]) == HARD_X_REGNUM) ? 3 : 4; + + int beq_offset = 2 + shiftlen + 2; + int bra_offset = shiftlen + 2 + cmplen + 2; + + sprintf (beq_pattern, "beq\t.+%d", beq_offset); + sprintf (bra_pattern, "bra\t.-%d", bra_offset); + + output_asm_insn ("pshs\t%2", operands); + output_asm_insn ("lea%2\t-1,%2", operands); + output_asm_insn ("cmp%2\t#-1", operands); + output_asm_insn (beq_pattern, operands); + if (shift1) + output_asm_insn (shift1, operands); + if (shift2) + output_asm_insn (shift2, operands); + output_asm_insn (bra_pattern, operands); + output_asm_insn ("puls\t%2", operands); +} + + +/** Generate RTL for the upper 8-bits of a 16-bit constant. */ +rtx +gen_rtx_const_high (rtx r) +{ + unsigned char v = (INTVAL (r) >> 8) & 0xFF; + signed char s = (signed char)v; + return gen_int_mode (s, QImode); +} + + +/** Generate RTL for the lower 8-bits of a 16-bit constant. */ +rtx +gen_rtx_const_low (rtx r) +{ + unsigned char v = INTVAL (r) & 0xFF; + signed char s = (signed char)v; + return gen_int_mode (s, QImode); +} + + +/** Generate RTL to allocate/free bytes on the stack. + * CODE is given as MINUS when allocating and PLUS when freeing, + * to match the semantics of a downward-growing stack. SIZE + * is always given as a positive integer. + */ +static rtx +gen_rtx_stack_adjust (enum rtx_code code, int size) +{ + if (size <= 0) + return NULL_RTX; + + if (code == MINUS) + size = -size; + + return gen_rtx_SET (Pmode, stack_pointer_rtx, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + gen_int_mode (size, HImode))); +} + + +/** Generate RTL to push/pop a set of registers. */ +rtx +gen_rtx_register_pushpop (int op, int regs) +{ + rtx nregs = gen_int_mode (regs, QImode); + + if (op == UNSPEC_PUSH_RS) + return gen_register_push (nregs); + else + return gen_register_pop (nregs); +} + + +/* Given a register set REGS, where the bit positions correspond to + * hard register numbers, return another bitmask that represents the + * order in which those registers would be pushed/popped. + * Registers that are pushed first have higher bit positions. + * The pop order is just the reverse bitmask. + * These values are the same as the bitmasks actually used in the + * machine instructions. */ +static unsigned int +register_push_order (int regs) +{ + unsigned int order = 0; + + if (REGSET_CONTAINS_P (HARD_PC_REGNUM, regs)) + order |= 0x80; + if (REGSET_CONTAINS_P (HARD_U_REGNUM, regs)) + order |= 0x40; + if (REGSET_CONTAINS_P (HARD_Y_REGNUM, regs)) + order |= 0x20; + if (REGSET_CONTAINS_P (HARD_X_REGNUM, regs)) + order |= 0x10; + if (REGSET_CONTAINS_P (HARD_DP_REGNUM, regs)) + order |= 0x8; + if (REGSET_CONTAINS_P (HARD_B_REGNUM, regs)) + order |= 0x4; + if (REGSET_CONTAINS_P (HARD_A_REGNUM, regs)) + order |= 0x2; + if (REGSET_CONTAINS_P (HARD_CC_REGNUM, regs)) + order |= 0x1; + + if (REGSET_CONTAINS_P (HARD_D_REGNUM, regs)) + order |= (0x4 | 0x2); + return order; +} + + +/* Returns nonzero if two consecutive push or pop instructions, + * as determined by the OP, can be merged into a single instruction. + * The first instruction in the sequence pushes/pops REGS1; the + * second applies to REGS2. + * + * If true, the resulting instruction can use (regs1 | regs2) + * safely. + */ +int +m6809_can_merge_pushpop_p (int op, int regs1, int regs2) +{ + /* Register sets must not overlap */ + if (regs1 & regs2) + return 0; + + if (op == UNSPEC_PUSH_RS) + return (register_push_order (regs1) > register_push_order (regs2)); + else if (op == UNSPEC_POP_RS) + return (register_push_order (regs1) < register_push_order (regs2)); + else + return 0; +} + + +/** Emit instructions for making a library call. + * MODE is the mode of the operation. + * NAME is the library function name. + * OPERANDS is the rtx array provided by the recognizer. + * COUNT is the number of input operands to the call, and + * should be 1 for a unary op or 2 for a binary op. + */ +void +emit_libcall_insns (enum machine_mode mode, + const char *name, + rtx *operands, + int count) +{ + /* Generate an rtx for the call target. */ + rtx symbol = gen_rtx_SYMBOL_REF (Pmode, name); + + /* Emit the library call. Slightly different based + on the number of operands */ + if (count == 2) + emit_library_call (symbol, LCT_NORMAL, mode, + 2, operands[1], mode, operands[2], mode); + else + emit_library_call (symbol, LCT_NORMAL, mode, + 1, operands[1], mode); + + /* The library call is expected to put its result + in LIBCALL_VALUE, so need to copy it into the destination. */ + emit_move_insn (operands[0], LIBCALL_VALUE(mode)); +} + + +/** + * A small helper function that writes out a single branch instruction. + * OPCODE is the short name, e.g. "ble". + * OPERANDS has the rtx for the target label. + * LONG_P is nonzero if we are emitting a long branch, and need to + * prepend an 'l' to the opcode name. + */ +void output_branch_insn1 (const char *opcode, rtx *operands, int long_p) +{ + char pattern[64]; + sprintf (pattern, "%s%s\t%%l0", long_p ? "l" : "", opcode); + output_asm_insn (pattern, operands); +} + +/** + * Output a branch/conditional branch insn of the proper + * length. code identifies the particular branch insn. + * operands holds the branch target in operands[0]. + * length says what the size of this insn should be. + * Based on the length, we know whether it should be a + * short (8-bit) or long (16-bit) branch. + */ +const char * +output_branch_insn (enum rtx_code code, rtx *operands, int length) +{ + int shortform; + + /* Decide whether or not to use the long or short form. + * Calculate automatically based on insn lengths. */ + shortform = ((length > 2) ? 0 : 1); + + /* Determine the proper opcode. + * Use the short (2-byte) opcode if the target is within + * reach. Otherwise, use jmp (3-byte opcode), unless + * compiling with -fpic, in which case we'll need to use + * lbra (4-byte opcode). + */ + switch (code) + { + case LABEL_REF: + if (shortform) + output_branch_insn1 ("bra", operands, 0); + else if (flag_pic) + output_branch_insn1 ("bra", operands, 1); + else + output_branch_insn1 ("jmp", operands, 0); + break; + case EQ: + output_branch_insn1 ("beq", operands, !shortform); + break; + case NE: + output_branch_insn1 ("bne", operands, !shortform); + break; + case GT: + output_branch_insn1 ("bgt", operands, !shortform); + break; + case GTU: + output_branch_insn1 ("bhi", operands, !shortform); + break; + case LT: + if (cc_prev_status.flags & CC_NO_OVERFLOW) + { + output_branch_insn1 ("bmi", operands, !shortform); + } + else + { + output_branch_insn1 ("blt", operands, !shortform); + } + break; + case LTU: + output_branch_insn1 ("blo", operands, !shortform); + break; + case GE: + if (cc_prev_status.flags & CC_NO_OVERFLOW) + { + output_branch_insn1 ("bpl", operands, !shortform); + } + else + { + output_branch_insn1 ("bge", operands, !shortform); + } + break; + case GEU: + output_branch_insn1 ("bhs", operands, !shortform); + break; + case LE: + if (cc_prev_status.flags & CC_NO_OVERFLOW) + { + output_branch_insn1 ("bmi", operands, !shortform); + output_branch_insn1 ("beq", operands, !shortform); + } + else + { + output_branch_insn1 ("ble", operands, !shortform); + } + break; + case LEU: + output_branch_insn1 ("bls", operands, !shortform); + break; + default: + abort(); + break; + } + return ""; +} + + +/** Returns the "cost" of an RTL expression. + * In general, the expression "COSTS_N_INSNS(1)" is used to represent + * the cost of a fast 8-bit arithmetic instruction that operates on + * a reg/mem or a reg/immed. Other costs are relative to this. + * + * Notes: + * - The cost of a REG is always zero; this cannot be changed. + * + * - On the 6809, instructions on two registers will nearly always take + * longer than those that operate on a register and a constant/memory, + * because of the way the instruction set is structured. + * + * TODO: multiply HImode by 2 should be done via shifts, instead of add. + */ +static bool +m6809_rtx_costs (rtx X, int code, int outer_code ATTRIBUTE_UNUSED, + int *total, bool speed) +{ + int has_const_arg = 0; + HOST_WIDE_INT const_arg; + enum machine_mode mode; + int nargs = 1; + rtx op0, op1; + + /* Data RTXs return a value between 0-3, depending on complexity. + All of these are less than COSTS_N_INSNS(1). */ + switch (code) + { + case CC0: + case PC: + *total = 0; + return true; + + case CONST_INT: + if (X == const0_rtx) + { + *total = 0; + return true; + } + else if ((unsigned) INTVAL (X) < 077) + { + *total = 1; + return true; + } + else + { + *total = 2; + return true; + } + + case LABEL_REF: case CONST: + *total = 2; + return true; + + case SYMBOL_REF: + /* References to memory are made cheaper if they have + * the 'direct' mode attribute set */ + *total = (SYMBOL_REF_FLAG (X)) ? 1 : 2; + return true; + + case MEM: + /* See what form of address was given */ + X = XEXP (X, 0); + switch (GET_CODE (X)) + { + case SYMBOL_REF: + *total = (SYMBOL_REF_FLAG (X)) ? 1 : 2; + break; + + case CONST_INT: + *total = 2; + break; + + case MEM: + *total = COSTS_N_INSNS (1) + 2; + break; + + default: + break; + } + return true; + + case CONST_DOUBLE: + /* TODO : not sure about this value. */ + *total = 3; + return true; + + default: + break; + } + + /* Decode the rtx */ + mode = GET_MODE (X); + op0 = XEXP (X, 0); + op1 = XEXP (X, 1); + + /* We don't implement anything in SImode or greater. */ + if (GET_MODE_SIZE (mode) >= GET_MODE_SIZE (SImode)) + { + *total = COSTS_N_INSNS (100); + return true; + } + + /* Figure out if there is a constant argument, and its value. */ + if (GET_RTX_CLASS (code) == RTX_BIN_ARITH + || GET_RTX_CLASS (code) == RTX_COMM_ARITH) + { + nargs = 2; + if (GET_CODE (op1) == CONST_INT) + { + has_const_arg = 1; + const_arg = INTVAL (op1); + } + } + + /* Penalize a reg/reg operation by adding MEMORY_MOVE_COST, + * Ignore soft registers, since these are really in memory. + * + * TODO: penalize HImode reg/reg for most operations, except maybe + * additions since index registers allow for that. + * + * TODO: shifts by constant N do not always require N instructions; + * some of this can be done cheaper. The number of actual insns can be + * predicted well. + */ + if (nargs == 2 && REAL_REG_P (op0) && REAL_REG_P (op1)) + { + *total = MEMORY_MOVE_COST (mode, Q_REGS, 0); + } + else + { + *total = 0; + } + + /* Operator RTXs are counted as COSTS_N_INSNS(N), where N is + the estimated number of actual machine instructions needed to + perform the computation. Some small adjustments are made since + some "instructions" are more complex than others. */ + switch (code) + { + case PLUS: case MINUS: case COMPARE: + /* 6809 handles these natively in QImode, and in HImode as long + * as operand 1 is constant. */ + if (mode == QImode || (mode == HImode && has_const_arg)) + *total += COSTS_N_INSNS (1); + else + *total += COSTS_N_INSNS (GET_MODE_SIZE (mode)); + + /* -1, 0, and 1 can be done using inherent instructions + * for PLUS and MINUS in QImode, so don't add extra cost. */ + if (has_const_arg + && (mode == QImode || mode == HImode) + && (const_arg == -1 || const_arg == 0 || const_arg == 1) + && (code == PLUS || code == MINUS)) + { + return true; + } + break; + + case AND: case IOR: case XOR: + case NEG: case NOT: + /* 6809 handles these natively in QImode, but requires + * splitting in HImode. Treat these as 2 insns. */ + *total += COSTS_N_INSNS (1) * GET_MODE_SIZE (mode); + break; + + case ASHIFT: case ASHIFTRT: case LSHIFTRT: + case ROTATE: case ROTATERT: + /* 6809 can do shift/rotates of a QImode by a constant in + * 1 insn times the shift count, or in HImode by a constant + * by splitting to 2 insns. + * + * Shift by a nonconstant will take significantly longer + * than any of these. */ + if (has_const_arg) + { + const_arg %= (GET_MODE_SIZE (mode) * 8); + if (const_arg == 0) + { + *total += COSTS_N_INSNS(1); + return true; + } + + /* HImode shifts greater than 8 get optimized due + * to register transfer from b to a; this cuts down the + * cost. */ + if (const_arg >= 8) + { + *total += COSTS_N_INSNS (1); + const_arg -= 8; + } + + /* The computed cost is 'const_arg' 1-bit shifts, doubled + if in HImode, minus the cost of the constant itself which + will be added in later but really shouldn't be. */ + *total += COSTS_N_INSNS (const_arg) * GET_MODE_SIZE (mode) - 1; + return true; + } + else + { + /* It may take up to 7 iterations of about 6-7 real + * instructions, so make this expensive. */ + *total += COSTS_N_INSNS (50); + } + break; + + case MULT: + { + /* Multiply is cheap when both arguments are 8-bits. They + could be QImode, or QImode widened to HImode, or a constant + that fits into 8-bits. As long as both operands qualify, + we can use a single mul instruction. + + Assume that fast multiply can be used, and change this if we find + differently... */ + int ok_for_qihi3 = 1; + + /* Check the first operand */ + switch (GET_MODE (op0)) + { + case QImode: + break; + case HImode: + if (GET_CODE (op0) != SIGN_EXTEND && GET_CODE (op0) != ZERO_EXTEND) + ok_for_qihi3 = 0; + break; + default: + ok_for_qihi3 = 0; + break; + } + + /* Likewise, check the second operand. This is where constants may appear. */ + switch (GET_MODE (op1)) + { + case QImode: + break; + case HImode: + if (GET_CODE (op1) != SIGN_EXTEND && GET_CODE (op1) != ZERO_EXTEND) + ok_for_qihi3 = 0; + break; + case VOIDmode: + if (!CONST_OK_FOR_LETTER_P (const_arg, 'K')) + ok_for_qihi3 = 0; + break; + default: + ok_for_qihi3 = 0; + break; + } + + /* Fast multiply takes about 4 times as many cycles as a normal + arithmetic operation. Otherwise, it will take an expensive libcall. */ + if (ok_for_qihi3) + *total += COSTS_N_INSNS (4); + else + *total = COSTS_N_INSNS (50); + break; + } + + case DIV: case UDIV: case MOD: case UMOD: + /* These all require more expensive libcalls. */ + *total += COSTS_N_INSNS (100); + break; + + /* TODO : TRUNCATE, SIGN_EXTEND, and ZERO_EXTEND */ + + /* These can normally be done with autoincrement, etc., so + * don't charge for them. */ + case PRE_DEC: + case PRE_INC: + case POST_DEC: + case POST_INC: + break; + + default: + break; + } + + /* Always return false, and let the caller gather the costs + * of the operands */ + return false; +} + + +static tree +m6809_handle_fntype_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_TYPE) + { + warning (WARNING_OPT "'%s' only valid for functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = TRUE; + } + + return NULL_TREE; +} + + +static tree +m6809_handle_data_type_attribute (tree *node ATTRIBUTE_UNUSED, + tree name ATTRIBUTE_UNUSED, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs ATTRIBUTE_UNUSED) +{ + return NULL_TREE; +} + + + +static tree +m6809_handle_default_attribute (tree *node ATTRIBUTE_UNUSED, + tree name ATTRIBUTE_UNUSED, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs ATTRIBUTE_UNUSED ) +{ + return NULL_TREE; +} + + +/* Table of valid machine attributes */ +const struct attribute_spec m6809_attribute_table[] = { /* +{ name, min, max, decl, type, fntype, handler } */ +{ "interrupt", 0, 0, false, true, true, m6809_handle_fntype_attribute }, +{ "naked", 0, 0, false, true, true, m6809_handle_fntype_attribute }, +{ "far", 0, 1, false, true, true, m6809_handle_fntype_attribute }, +{ "bank", 0, 1, true, false, false, m6809_handle_default_attribute }, +{ "boolean", 0, 0, false, true, false, m6809_handle_data_type_attribute }, +{ NULL, 0, 0, false, true, false, NULL }, +}; + + +/** Initialize builtin routines for the 6809. */ +void +m6809_init_builtins (void) +{ + /* Create type trees for each function signature required. + * + * void_ftype_void = void f(void) + * void_ftype_uchar = void f(unsigned char) + * uchar_ftype_uchar2 = unsigned char f (unsigned char, unsigned char) + */ + tree void_ftype_void = + build_function_type (void_type_node, void_list_node); + + tree void_ftype_uchar = + build_function_type (void_type_node, + tree_cons (NULL_TREE, unsigned_char_type_node, void_list_node)); + + tree uchar_ftype_uchar2 = + build_function_type (unsigned_char_type_node, + tree_cons (NULL_TREE, unsigned_char_type_node, + tree_cons (NULL_TREE, unsigned_char_type_node, void_list_node))); + + /* Register each builtin function. */ + add_builtin_function ("__builtin_swi", void_ftype_void, + M6809_SWI, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_swi2", void_ftype_void, + M6809_SWI2, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_swi3", void_ftype_void, + M6809_SWI3, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_cwai", void_ftype_uchar, + M6809_CWAI, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_sync", void_ftype_void, + M6809_SYNC, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_nop", void_ftype_void, + M6809_NOP, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_blockage", void_ftype_void, + M6809_BLOCKAGE, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_add_decimal", uchar_ftype_uchar2, + M6809_ADD_DECIMAL, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_add_carry", uchar_ftype_uchar2, + M6809_ADD_CARRY, BUILT_IN_MD, NULL, NULL_TREE); + + add_builtin_function ("__builtin_sub_carry", uchar_ftype_uchar2, + M6809_SUB_CARRY, BUILT_IN_MD, NULL, NULL_TREE); +} + + +/** Used by m6809_expand_builtin, given a tree ARGLIST which + * refers to the operands of a builtin call, return an rtx + * that represents the nth operand, as denoted by OPNUM, which + * is a zero-based integer. MODE gives the expected mode + * of the operand. + * + * This rtx is suitable for use in the emitted RTL for the + * builtin instruction. */ +rtx +m6809_builtin_operand (tree arglist, enum machine_mode mode, int opnum) +{ + tree arg; + rtx r; + + arg = CALL_EXPR_ARG (arglist, opnum); + + /* Convert the tree to RTL */ + r = expand_expr (arg, NULL_RTX, mode, EXPAND_NORMAL); + if (r == NULL_RTX) + return NULL_RTX; + return r; +} + + +/** Expand a builtin that was registered in init_builtins into + * RTL. */ +rtx +m6809_expand_builtin (tree exp, + rtx target, + rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED ) +{ + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + tree arglist = exp; + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + rtx r0, r1; + + switch (fcode) + { + case M6809_SWI: + r0 = gen_rtx_CONST_INT (VOIDmode, 1); + emit_insn (target = gen_m6809_swi (r0)); + return target; + + case M6809_SWI2: + r0 = gen_rtx_CONST_INT (VOIDmode, 2); + emit_insn (target = gen_m6809_swi (r0)); + return target; + + case M6809_SWI3: + r0 = gen_rtx_CONST_INT (VOIDmode, 3); + emit_insn (target = gen_m6809_swi (r0)); + return target; + + case M6809_CWAI: + r0 = m6809_builtin_operand (arglist, QImode, 0); + emit_insn (target = gen_m6809_cwai (r0)); + return target; + + case M6809_SYNC: + emit_insn (target = gen_m6809_sync ()); + return target; + + case M6809_ADD_CARRY: + r0 = m6809_builtin_operand (arglist, QImode, 0); + r1 = m6809_builtin_operand (arglist, QImode, 1); + if (!target) + target = gen_reg_rtx (QImode); + emit_insn (gen_addqi3_carry (target, r0, r1)); + return target; + + case M6809_SUB_CARRY: + r0 = m6809_builtin_operand (arglist, QImode, 0); + r1 = m6809_builtin_operand (arglist, QImode, 1); + if (!target) + target = gen_reg_rtx (QImode); + emit_insn (gen_subqi3_carry (target, r0, r1)); + return target; + + case M6809_NOP: + emit_insn (target = gen_nop ()); + return target; + + case M6809_BLOCKAGE: + emit_insn (target = gen_blockage ()); + return target; + + case M6809_ADD_DECIMAL: + r0 = m6809_builtin_operand (arglist, QImode, 0); + r1 = m6809_builtin_operand (arglist, QImode, 1); + if (!target) + target = gen_reg_rtx (QImode); + emit_insn (gen_addqi3_decimal (target, r0, r1)); + return target; + + default: + warning (WARNING_OPT "unknown builtin expansion ignored"); + return NULL_RTX; + } +} + + + +/* Returns nonzero if 'x' represents a function that was declared + * as __noreturn__. */ +int +noreturn_functionp (rtx x) +{ + tree decl = call_target_decl (x); + + if (decl == NULL_TREE) + return 0; + else + return TREE_THIS_VOLATILE (decl); +} + + +const char * +far_function_type_p (tree type) +{ + tree attr; + const char *page; + + /* Return whether or not this decl has the far attribute */ + attr = lookup_attribute ("far", TYPE_ATTRIBUTES (type)); + if (attr == NULL_TREE) + return NULL; + + /* If it is far, check for a value */ + attr = TREE_VALUE (attr); + if (attr == NULL_TREE) + { + warning (WARNING_OPT "far code page not specified, using local value"); + return far_code_page; + } + + /* We have a TREE_LIST of attribute values, get the first one. + * It should be an INTEGER_CST. */ + attr = TREE_VALUE (attr); + page = TREE_STRING_POINTER (attr); + return page; +} + + +/* For a far function, returns the identifier that states which page + * it resides in. Otherwise, returns NULL for ordinary functions. */ +const char * +far_functionp (rtx x) +{ + tree decl, decl_type; + const char *page; + + /* Find the FUNCTION_DECL corresponding to the rtx being called. */ + decl = call_target_decl (x); + if (decl == NULL_TREE) + return NULL; + + /* See if the function has the new 'banked' attribute. These + * are numeric instead of text */ + page = m6809_get_decl_bank (decl); + if (page) + return page; + + /* No, lookup the type of the function and see if the type + * specifies far or not. */ + decl_type = TREE_TYPE (decl); + if (decl_type == NULL_TREE) + return NULL; + return far_function_type_p (decl_type); +} + + + +/** Outputs the assembly language for a far call. */ +void +output_far_call_insn (rtx *operands, int has_return) +{ + static char page_data[64]; + const char *called_page; + + /* The logic is the same for functions whether or not there + * is a return value. Skip over the return value in this + * case, so that the call location is always operands[0]. */ + if (has_return) + operands++; + + /* Get the name of the page being called */ + called_page = far_functionp (operands[0]); + +#if 0 /* TODO : broken logic */ + /* See if the called page name is a 'bank' */ + if (isdigit (*called_page)) + { + /* New style banking */ + if (!strcmp (called_page, current_bank_name)) + { + /* Same page */ + output_asm_insn ("jsr\t%0", operands); + } + else + { + /* Different page */ + output_asm_insn ("jsr\t__far_call_handler\t;new style", operands); + output_asm_insn ("\t.dw\t%0", operands); + sprintf (page_data, "\t.db\t%s", called_page); + output_asm_insn (page_data, operands); + } + return; + } +#endif + + /* Are we calling a different page than we are running in? */ + if (!strcmp (called_page, far_code_page)) + { + /* Same page : no need to execute a far call */ + if (flag_pic) + output_asm_insn ("lbsr\t%C0", operands); + else + output_asm_insn ("jsr\t%0", operands); + } + else + { + /* Different page : need to emit far call thunk */ + + /* First output a call to the thunk for making far calls. */ + if (flag_pic) + output_asm_insn ("lbsr\t__far_call_handler", operands); + else + output_asm_insn ("jsr\t__far_call_handler\t;old style", operands); + + /* Now output the name of the call site */ + output_asm_insn ("\t.dw\t%C0", operands); + + /* Finally output the page number */ + sprintf (page_data, "\t.db\t%s", far_functionp (operands[0])); + output_asm_insn (page_data, operands); + } +} + + +int +m6809_init_cumulative_args (CUMULATIVE_ARGS cum ATTRIBUTE_UNUSED, + tree fntype, + rtx libname ATTRIBUTE_UNUSED) +{ + cum = 0; + + /* For far functions, the current implementation does not allow for + * stack parameters. So note whenever the called function is far + * and in a different page than the current one; such a function + * should give an error if a stack parameter is generated. */ + if (fntype) + { + const char *called_page = far_function_type_p (fntype); + if (called_page && strcmp (called_page, far_code_page) && !TARGET_FAR_STACK_PARAM) + cum |= CUM_STACK_INVALID; + } + + if (fntype && TYPE_ARG_TYPES (fntype) != 0 && + (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) + { + /* has variable arguments, cannot use registers */ + cum |= (CUM_X_MASK | CUM_B_MASK | CUM_STACK_ONLY); + } + + if (m6809_abi_version == M6809_ABI_VERSION_STACK) + { + /* cannot use registers ; only use the stack */ + cum |= (CUM_STACK_ONLY | CUM_X_MASK | CUM_B_MASK); + } + + return cum; +} + + +rtx +m6809_function_arg_on_stack (CUMULATIVE_ARGS *cump) +{ + if (*cump & CUM_STACK_INVALID) + { + *cump &= ~CUM_STACK_INVALID; + error ("far function needs stack, will not work"); + } + return NULL_RTX; +} + +void m6809_asm_trampoline_template(FILE *f) +{ + fprintf(f, "ldy #0000\n"); + fprintf(f, "jmp 0x0000\n"); +} + +/* + * Trampoline output: + * + * ldu #&cxt 4 bytes --LDY- ?? ?? + * jmp fnaddr 3 bytes JMP ?? ?? + */ +void +m6809_initialize_trampoline (rtx tramp, tree fndecl, rtx cxt) +{ + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + /* TODO - optimize by generating the entire trampoline code here, + * and removing the template altogether, since there are only two + * bytes there that matter. */ + emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt); + emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 5)), fnaddr); +} + + +/** Echo the version of the compiler and the name of the source file + * at the beginning of each assembler output file. asm_out_file + * is a global FILE * pointing to the output stream. */ +void +m6809_asm_file_start (void) +{ + const char *module_name; + + fprintf (asm_out_file, "\n;;; gcc for m6809 : %s %s\n", + __DATE__, __TIME__); + fprintf (asm_out_file, ";;; %s\n", version_string); + + fprintf (asm_out_file, ";;; ABI version %d\n", m6809_abi_version); + fprintf (asm_out_file, ";;; %s\n", + (TARGET_BYTE_INT ? "-mint8" : "-mint16")); + if (TARGET_EXPERIMENT) + fprintf (asm_out_file, ";;; -mexperiment\n"); + if (TARGET_WPC) + fprintf (asm_out_file, ";;; -mwpc\n"); + if (TARGET_6309) + fprintf (asm_out_file, ";;; -m6309\n"); + + /* Print the name of the module, which is taken as the base name + * of the input file. + * See the 'User-Defined Symbols' section of the assembler + * documentation for the rules on valid symbols. + */ + module_name = lbasename (main_input_filename); + + fprintf (asm_out_file, "\t.module\t"); + + if (*module_name >= '0' && *module_name <= '9') + fprintf (asm_out_file, "_"); + + while (*module_name) + { + if ((*module_name >= '0' && *module_name <= '9') + || (*module_name >= 'A' && *module_name <= 'Z') + || (*module_name >= 'a' && *module_name <= 'z') + || *module_name == '$' + || *module_name == '.' + || *module_name == '_') + { + fprintf (asm_out_file, "%c", *module_name); + } + else + { + fprintf (asm_out_file, "_"); + } + module_name++; + } + + fprintf (asm_out_file, "\n"); +} + + +/** Returns true if prologue/epilogue code is required for the + * current function being compiled. + * + * This is just the inverse of whether the function is declared as + * 'naked'. + */ +int +prologue_epilogue_required (void) +{ + return !m6809_current_function_has_type_attr_p ("naked") + && !m6809_current_function_has_type_attr_p ("noreturn"); +} + + +/** Expand RTL for function entry */ +void +emit_prologue_insns (void) +{ + rtx insn; + unsigned int live_regs = m6809_get_live_regs (); + unsigned int frame_size = get_frame_size (); + + /* Save all registers used, including the frame pointer */ + if (live_regs && !m6809_current_function_has_type_attr_p ("interrupt")) + { + insn = emit_insn ( + gen_rtx_register_pushpop (UNSPEC_PUSH_RS, live_regs)); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Allocate space for local variables */ + if (frame_size != 0) + { + insn = emit_insn (gen_rtx_stack_adjust (MINUS, frame_size)); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Set the frame pointer if it is needed */ + if (frame_pointer_needed) + { + insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } +} + + +/** Expand RTL for function exit */ +void +emit_epilogue_insns (bool sibcall_p) +{ + unsigned int live_regs = m6809_get_live_regs (); + unsigned int frame_size = get_frame_size (); + + if (frame_size != 0) + emit_insn (gen_rtx_stack_adjust (PLUS, frame_size)); + + if (sibcall_p) + { + if (live_regs) + emit_insn (gen_rtx_register_pushpop (UNSPEC_POP_RS, live_regs)); + } + else + { + if (live_regs && !m6809_current_function_has_type_attr_p ("interrupt")) + emit_insn ( + gen_rtx_register_pushpop (UNSPEC_POP_RS, PC_REGBIT | live_regs)); + + if (m6809_current_function_has_type_attr_p ("interrupt")) + emit_jump_insn (gen_return_rti ()); + else + emit_jump_insn (gen_return_rts ()); + } +} + +#if 0 +/** Predefine some preprocessor names according to the currently + * selected compiler options */ +void +m6809_cpu_cpp_builtins (void) +{ + if (TARGET_6309) + { + builtin_define_std ("__M6309__"); + builtin_define_std ("__m6309__"); + } + else + { + builtin_define_std ("__M6809__"); + builtin_define_std ("__m6809__"); + } + + if (TARGET_BYTE_INT) + builtin_define_std ("__int8__"); + else + builtin_define_std ("__int16__"); + + switch (m6809_abi_version) + { + case M6809_ABI_VERSION_STACK: + builtin_define_std ("__regargs__"); + builtin_define_std ("__ABI_STACK__"); + break; + case M6809_ABI_VERSION_REGS: + builtin_define_std ("__ABI_REGS__"); + break; + case M6809_ABI_VERSION_BX: + builtin_define_std ("__ABI_BX__"); + break; + default: + break; + } + + if (TARGET_WPC) + builtin_define_std ("__WPC__"); + + if (TARGET_DRET) + builtin_define_std ("__DRET__"); +} +#endif + +#define MAX_ASM_ASCII_STRING 48 + +void +m6809_output_ascii (FILE *fp, const char *str, unsigned long size) +{ + unsigned long i; + bool use_ascii = true; + + /* If the size is too large, then break this up into multiple + outputs. The assembler can only output roughly 48 bytes at a + time. Note that if there are lots of escape sequences in + the string, this may fail. */ + if (size > MAX_ASM_ASCII_STRING) + { + m6809_output_ascii (fp, str, MAX_ASM_ASCII_STRING); + m6809_output_ascii (fp, str + MAX_ASM_ASCII_STRING, + size - MAX_ASM_ASCII_STRING); + return; + } + + /* Check for 8-bit codes, which cannot be embedded in an .ascii */ + for (i = 0; i < size; i++) + { + int c = str[i] & 0377; + if (c >= 0x80) + { + use_ascii = false; + break; + } + } + + if (use_ascii) + fprintf (fp, "\t.ascii \""); + + for (i = 0; i < size; i++) + { + int c = str[i] & 0377; + + if (use_ascii) + { + /* Just output the plain character if it is printable, + otherwise output the escape code for the character. + The assembler recognizes the same C-style octal escape sequences, + except that it only supports 7-bit codes. */ + if (c >= ' ' && c < 0177 && c != '\\' && c != '"') + putc (c, fp); + else switch (c) + { + case '\n': +#ifndef TARGET_COCO + fputs ("\\n", fp); + break; +#endif + /* On the CoCo, we fallthrough and treat '\n' like '\r'. */ + case '\r': + fputs ("\\r", fp); + break; + case '\t': + fputs ("\\t", fp); + break; + case '\f': + fputs ("\\f", fp); + break; + case 0: + fputs ("\\0", fp); + break; + default: + fprintf (fp, "\\%03o", c); + break; + } + } + else + { + fprintf (fp, "\t.byte\t0x%02X\n", c); + } + } + + if (use_ascii) + fprintf (fp, "\"\n"); +} + + +void +m6809_output_quoted_string (FILE *asm_file, const char *string) +{ + char c; + + if (strlen (string) > MAX_ASM_ASCII_STRING) + { + /* The string length is too large. We'll have to truncate it. + This is only called from debugging functions, so it's usually + not critical. */ + + char truncated_string[MAX_ASM_ASCII_STRING+1]; + + /* Copy as many characters as we can. */ + strncpy (truncated_string, string, MAX_ASM_ASCII_STRING); + truncated_string[MAX_ASM_ASCII_STRING] = '\0'; + string = truncated_string; + } + + /* Copied from toplev.c */ + + putc ('\"', asm_file); + while ((c = *string++) != 0) { + if (ISPRINT (c)) { + if (c == '\"' || c == '\\') + putc ('\\', asm_file); + putc (c, asm_file); + } + else + fprintf (asm_file, "\\%03o", (unsigned char) c); + } + putc ('\"', asm_file); +} + + +/** Output the assembly code for a shift instruction where the + * shift count is not constant. */ +void +m6809_output_shift_insn (int rtx_code, rtx *operands) +{ + struct shift_opcode *op; + + if (GET_CODE (operands[2]) == CONST_INT) + abort (); + + if (optimize_size && GET_MODE (operands[0]) == HImode) + { + switch (rtx_code) + { + case ASHIFT: + output_asm_insn ("jsr\t_ashlhi3", operands); + break; + case ASHIFTRT: + output_asm_insn ("jsr\t_ashrhi3", operands); + break; + case LSHIFTRT: + output_asm_insn ("jsr\t_lshrhi3", operands); + break; + } + } + else if (GET_MODE (operands[0]) == HImode) + { + switch (rtx_code) + { + case ASHIFT: + m6809_gen_register_shift (operands, "aslb", "rola"); + break; + case ASHIFTRT: + m6809_gen_register_shift (operands, "asra", "rorb"); + break; + case LSHIFTRT: + m6809_gen_register_shift (operands, "lsra", "rorb"); + break; + } + } + else + { + switch (rtx_code) + { + case ASHIFT: + m6809_gen_register_shift (operands, "aslb", NULL); + break; + case ASHIFTRT: + m6809_gen_register_shift (operands, "asrb", NULL); + break; + case LSHIFTRT: + m6809_gen_register_shift (operands, "lsrb", NULL); + break; + } + } +} + + +void +m6809_emit_move_insn (rtx dst, rtx src) +{ + emit_insn (gen_rtx_SET (VOIDmode, dst, src)); + if (ACC_A_REG_P (dst)) + emit_insn (gen_rtx_USE (VOIDmode, dst)); +} + + +/** Split a complex shift instruction into multiple CPU + * shift instructions. */ +void +m6809_split_shift (enum rtx_code code, rtx *operands) +{ + enum machine_mode mode; + int count; + + mode = GET_MODE (operands[0]); + count = INTVAL (operands[2]); + + /* Handle a shift count outside the range of 0 .. N-1, where + * N is the mode size in bits. We normalize the count, and + * for negative counts we also invert the direction of the + * shift. */ + if ((count < 0) || (count >= 8 * GET_MODE_SIZE (mode))) + { + if (count < 0) + { + count = -count; + code = (code == ASHIFT) ? ASHIFTRT : ASHIFT; + } + count %= (8 * GET_MODE_SIZE (mode)); + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (code, mode, operands[1], + gen_rtx_CONST_INT (VOIDmode, count))); + } + + /* Handle shift by zero explicitly as a no-op. */ + if (count == 0) + { + emit_insn (gen_nop ()); + return; + } + + /* Decompose the shift by a constant N > 8 into two + * shifts, first by 8 and then by N-8. + * This "speeds up" the process for large shifts that would be + * handled below, but allows for some optimization. + * In some cases shift by 8 can be implemented fast. If an + * instruction to shift by 8 is defined, it will be used here; + * otherwise it will be further decomposed as below. */ + if (mode == HImode && count > 8) + { + rtx output = operands[0]; + + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (code, mode, operands[1], + gen_rtx_CONST_INT (VOIDmode, 8))); + + /* Unsigned shifts always produce a zero in either the + * upper or lower half of the output; then, that part + * does not need to be shifted anymore. We modify the + * output and the subsequent instructions to operate in + * QImode only on the relevant part. */ + if (REG_P (output)) + { + if (code == ASHIFT) + { + output = gen_rtx_REG (QImode, HARD_A_REGNUM); + mode = QImode; + } + else + { + output = gen_rtx_REG (QImode, HARD_D_REGNUM); + mode = QImode; + } + } + + m6809_emit_move_insn (output, + gen_rtx_fmt_ee (code, mode, copy_rtx (output), + gen_rtx_CONST_INT (VOIDmode, count-8))); + return; + } + + /* Rewrite the unsigned shift of an 8-bit register by a large constant N + * (near to the maximum of 8) as a rotate and mask. */ + if (mode == QImode && REG_P (operands[0]) && count >= ((code == ASHIFTRT) ? 7 : 6)) + { + unsigned int mask; + unsigned int was_signed = (code == ASHIFTRT); + + code = (code == ASHIFT) ? ROTATERT : ROTATE; + if (code == ROTATE) + mask = (count == 6) ? 0x03 : 0x01; + else + mask = (count == 6) ? 0xC0 - 0x100 : 0x80 - 0x100; + count = 9 - count; + + do { + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (code, QImode, operands[1], const1_rtx)); + } while (--count != 0); + + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (AND, QImode, operands[1], + gen_rtx_CONST_INT (VOIDmode, mask))); + + if (was_signed) + { + emit_insn (gen_negqi2 (operands[0], copy_rtx (operands[0]))); + if (ACC_A_REG_P (operands[0])) + emit_insn (gen_rtx_USE (VOIDmode, operands[0])); + } + return; + } + + /* Decompose the shift by any constant N > 1 into a sequence + * of N shifts. + * This is done recursively, by creating a shift by 1 and a + * shift by N-1, as long as N>1. */ + if (count > 1) + { + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (code, mode, operands[1], const1_rtx)); + + m6809_emit_move_insn (operands[0], + gen_rtx_fmt_ee (code, mode, operands[1], + gen_rtx_CONST_INT (VOIDmode, count-1))); + return; + } + + /* Decompose the single shift of a 16-bit quantity into two + * CPU instructions, one for each 8-bit half. + */ + if (mode == HImode && count == 1) + { + rtx first, second; + enum rtx_code rotate_code; + + rotate_code = (code == ASHIFT) ? ROTATE : ROTATERT; + + /* Split the operand into two 8-bit entities. + * FIRST is the one that will get shifted via a regular CPU + * instruction. + * SECOND is the one that will have the result of the first shift + * rotated in. + * + * We initialize first and second as if we are doing a left shift, + * then swap the operands if it's a right shift. + */ + if (REG_P (operands[0])) + { + first = gen_rtx_REG (QImode, HARD_D_REGNUM); /* HARD_B_REGNUM? */ + second = gen_rtx_REG (QImode, HARD_A_REGNUM); + } + else + { + first = adjust_address (operands[0], QImode, 1); + second = adjust_address (operands[0], QImode, 0); + } + + if (rotate_code == ROTATERT) + { + rtx tmp; tmp = first; first = second; second = tmp; + } + + /* Decompose into a shift and a rotate instruction. */ + m6809_emit_move_insn (first, + gen_rtx_fmt_ee (code, QImode, copy_rtx (first), const1_rtx)); + m6809_emit_move_insn (second, + gen_rtx_fmt_ee (rotate_code, QImode, copy_rtx (second), const1_rtx)); + return; + } +} + + +/** Adjust register usage based on compile-time flags. */ +void +m6809_conditional_register_usage (void) +{ + unsigned int soft_regno; + +#ifdef CONFIG_SOFT_REGS_ALWAYS + m6809_soft_regs = CONFIG_SOFT_REGS_ALWAYS; +#else + if (!m6809_soft_reg_count) + return; + m6809_soft_regs = atoi (m6809_soft_reg_count); +#endif + + if (m6809_soft_regs == 0) + return; + + if (m6809_soft_regs > NUM_M_REGS) + m6809_soft_regs = NUM_M_REGS; + + /* Registers are marked FIXED by default. Free up if + the user wishes. */ + for (soft_regno = 1; soft_regno < m6809_soft_regs; soft_regno++) + { + fixed_regs[SOFT_M0_REGNUM + soft_regno] = 0; + + /* Mark the softregs as call-clobbered, so that they need + * not be saved/restored on function entry/exit. */ + call_used_regs[SOFT_M0_REGNUM + soft_regno] = 1; + } +} + + +/** Return a RTX representing how to return a value from a function. + VALTYPE gives the type of the value, FUNC identifies the function + itself. + + In general, we only care about the width of the result. */ +rtx +m6809_function_value (const tree valtype, const tree func ATTRIBUTE_UNUSED) +{ + unsigned int regno; + enum machine_mode mode; + + /* Get the mode (i.e. width) of the result. */ + mode = TYPE_MODE (valtype); + + if (lookup_attribute ("boolean", TYPE_ATTRIBUTES (valtype))) + regno = HARD_Z_REGNUM; + else if (mode == QImode || (TARGET_DRET && mode == HImode)) + regno = HARD_D_REGNUM; + else + regno = HARD_X_REGNUM; + return gen_rtx_REG (mode, regno); +} + + +/** Return 1 if REGNO is possibly needed to return the result +of a function, 0 otherwise. */ +int +m6809_function_value_regno_p (unsigned int regno) +{ + if (regno == HARD_Z_REGNUM) + return 1; + else if ((TARGET_BYTE_INT || TARGET_DRET) && regno == HARD_D_REGNUM) + return 1; + else if (!TARGET_DRET && regno == HARD_X_REGNUM) + return 1; + else + return 0; +} + + +#ifdef TRACE_PEEPHOLE +int +m6809_match_peephole2 (unsigned int peephole_id, unsigned int stage) +{ + if (stage == PEEP_END) + { + printf ("%s: peephole %d pattern and predicate matched\n", + main_input_filename, peephole_id); + fflush (stdout); + } + else if (stage == PEEP_COND) + { + printf ("%s: peephole %d? at least pattern matched\n", + main_input_filename, peephole_id); + fflush (stdout); + } + return 1; +} +#else +int +m6809_match_peephole2 (unsigned int peephole_id ATTRIBUTE_UNUSED, + unsigned int stage ATTRIBUTE_UNUSED) +{ + return 1; +} +#endif /* TRACE_PEEPHOLE */ + + +/** Return 1 if it is OK to store a value of MODE in REGNO. */ +int +m6809_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) +{ + /* Soft registers, as they are just memory, can really hold + values of any type. However we restrict them to values of + size HImode or QImode to prevent exhausting them for larger + values. + Word values cannot be placed into the first soft register, + as it is the low byte that is being placed there, which + corrupts the (non-soft) register before it. */ + if (M_REGNO_P (regno)) + { + switch (GET_MODE_SIZE (mode)) + { + case 1: + return 1; + case 2: + return regno != SOFT_M0_REGNUM; + default: + return 0; + } + } + + /* VOIDmode can be stored anywhere */ + else if (mode == VOIDmode) + return 1; + + /* Zero is a reserved register, but problems occur if we don't + say yes here??? */ + else if (regno == 0) + return 1; + + /* For other registers, return true only if the requested size + exactly matches the hardware size. */ + else if ((G_REGNO_P (regno)) && (GET_MODE_SIZE (mode) == 2)) + return 1; + else if ((BYTE_REGNO_P (regno)) && (GET_MODE_SIZE (mode) == 1)) + return 1; + else + return 0; +} + + +/* exp is the call expression. DECL is the called function, + * or NULL for an indirect call */ +bool +m6809_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) +{ + tree type, arg; + const char *name; + bool result = 0; + int argcount = 0; + int step = 1; + + /* If there is no DECL, it is an indirect call. + * Never optimize this??? */ + if (decl == NULL) + goto done; + + /* Never allow an interrupt handler to be optimized this way. */ + if (m6809_function_has_type_attr_p (decl, "interrupt")) + goto done; + + /* Skip sibcall if the type can't be found for + * some reason */ + step++; + name = IDENTIFIER_POINTER (DECL_NAME (decl)); + type = TREE_TYPE (decl); + if (type == NULL) + goto done; + + /* Skip sibcall if the target is a far function */ + step++; + if (far_function_type_p (type) != NULL) + goto done; + + /* Skip sibcall if the called function's arguments are + * variable */ + step++; + if (TYPE_ARG_TYPES (type) == NULL) + goto done; + + /* Allow sibcalls in other cases. */ + result = 1; +done: + /* printf ("%s ok for sibcall? %s, step %d, args %d\n", name, result ? "yes" : "no", step, argcount); */ + return result; +} + + +/** Emit code for the 'casesi' pattern. + * This pattern is only used in 8-bit mode, and can be disabled + * with -mold-case there as well. The rationale for this is to + * do a better job than the simpler but well-tested 'tablejump' + * method. + * + * For small jumptables, where the switch expression is an + * 8-bit value, the lookup can be done more efficiently + * using the "B,X" style index mode. */ +void +m6809_do_casesi (rtx index, rtx lower_bound, rtx range, + rtx table_label, rtx default_label) +{ + enum machine_mode mode; + rtx scaled; + rtx table_in_reg; + + /* expr.c has to be patched so that it does not promote + * the expression to SImode, but rather to HImode. + * Fail now if that isn't the case. */ + if (GET_MODE_SIZE (GET_MODE (index)) > GET_MODE_SIZE (HImode)) + error ("try_casesi promotion bug"); + + /* Determine whether or not we are going to work primarily in + * QImode or HImode. This depends on the size of the index + * into the lookup table. QImode can only be used when the + * index is less than 0x40, since it will be doubled but + * must remain unsigned. */ + if ((GET_CODE (range) == CONST_INT) && (INTVAL (range) < 0x40)) + mode = QImode; + else + mode = HImode; + + /* Convert to QImode if necessary */ + if (mode == QImode) + { + index = gen_lowpart_general (mode, index); + lower_bound = gen_lowpart_general (mode, lower_bound); + } + + /* Translate from case value to table index by subtraction */ + if (lower_bound != const0_rtx) + index = expand_binop (mode, sub_optab, index, lower_bound, + NULL_RTX, 0, OPTAB_LIB_WIDEN); + + /* Emit compare-and-jump to test for index out-of-range */ + emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, mode, 1, + default_label); + + /* Put the table address is in a register */ + table_in_reg = gen_reg_rtx (Pmode); + emit_move_insn (table_in_reg, gen_rtx_LABEL_REF (Pmode, table_label)); + + /* Emit table lookup and jump */ + if (mode == QImode) + { + /* Scale the index */ + scaled = gen_reg_rtx (QImode); + emit_insn (gen_ashlqi3 (scaled, index, const1_rtx)); + + /* Emit the jump */ + emit_jump_insn (gen_tablejump_short_offset (scaled, table_in_reg)); + } + else + { + /* Scale the index */ + emit_insn (gen_ashlhi3 (index, index, const1_rtx)); + + /* Emit the jump */ + emit_jump_insn (gen_tablejump_long_offset (index, table_in_reg)); + } + + /* Copied from expr.c */ + if (!CASE_VECTOR_PC_RELATIVE && !flag_pic) + emit_barrier (); +} + + +/** Output the assembly code for a 32-bit add/subtract. */ +void +m6809_output_addsi3 (int rtx_code, rtx *operands) +{ + rtx xoperands[8]; + rtx dst = operands[0]; + + /* Prepare the operands by splitting each SImode into two HImodes + that can be operated independently. The high word of operand 1 + is further divided into two QImode components for use with 'adc' + style instructions. */ + xoperands[7] = operands[3]; + + xoperands[0] = adjust_address (dst, HImode, 2); + xoperands[3] = adjust_address (dst, HImode, 0); + +#if 1 + xoperands[2] = adjust_address (operands[1], HImode, 2); + xoperands[6] = adjust_address (operands[1], HImode, 0); + + /* Operand 2 may be a MEM or a CONST_INT */ + if (GET_CODE (operands[2]) == CONST_INT) + { + xoperands[1] = gen_int_mode (INTVAL (operands[2]) & 0xFFFF, HImode); + xoperands[4] = gen_int_mode ((INTVAL (operands[2]) >> 24) & 0xFF, QImode); + xoperands[5] = gen_int_mode ((INTVAL (operands[2]) >> 16) & 0xFF, QImode); + } + else + { + xoperands[1] = adjust_address (operands[2], HImode, 2); + xoperands[4] = adjust_address (operands[2], QImode, 0); + xoperands[5] = adjust_address (operands[2], QImode, 1); + } + +#endif + +#if 0 + xoperands[1] = adjust_address (operands[1], HImode, 2); + xoperands[4] = adjust_address (operands[1], QImode, 0); + xoperands[5] = adjust_address (operands[1], QImode, 1); + + /* Operand 2 may be a MEM or a CONST_INT */ + if (GET_CODE (operands[2]) == CONST_INT) + { + xoperands[2] = gen_int_mode ((INTVAL (operands[2])) & 0xFFFF, HImode); + xoperands[6] = gen_int_mode ((INTVAL (operands[2]) >> 16) & 0xFFFF, HImode); + } + else + { + xoperands[2] = adjust_address (operands[2], HImode, 2); + xoperands[6] = adjust_address (operands[2], HImode, 0); + } +#endif + + /* Output the assembly code. */ + if (rtx_code == PLUS) + { + output_asm_insn ("ld%7\t%2", xoperands); + output_asm_insn ("add%7\t%1", xoperands); + output_asm_insn ("st%7\t%0", xoperands); + output_asm_insn ("ld%7\t%6", xoperands); + output_asm_insn ("adcb\t%5", xoperands); + output_asm_insn ("adca\t%4", xoperands); + output_asm_insn ("st%7\t%3", xoperands); + } + else + { + output_asm_insn ("ld%7\t%2", xoperands); + output_asm_insn ("sub%7\t%1", xoperands); + output_asm_insn ("st%7\t%0", xoperands); + output_asm_insn ("ld%7\t%6", xoperands); + output_asm_insn ("sbcb\t%5", xoperands); + output_asm_insn ("sbca\t%4", xoperands); + output_asm_insn ("st%7\t%3", xoperands); + } +} + + +#if 0 +/** Output the assembly code for a 32-bit shift. +Operands 0 and 1 must be the same rtx, forced by a matching +constraint. Operand 2 must be a CONST_INT. Operand 3 is +"d" in case a temporary reg is needed. */ +void +m6809_output_shiftsi3 (int rtx_code, rtx *operands) +{ + unsigned int count = INTVAL (operands[2]) % 32; + unsigned int size = 4; /* sizeof (SImode) */ + int s; + rtx xoperands[4]; + int op; + int start, end, step; + + /* Initialize */ + if (rtx_code == ASHIFT) + { + start = size-1; + end = -1; + step = -1; + } + else + { + start = 0; + end = size; + step = 1; + } + + xoperands[2] = operands[2]; + xoperands[3] = operands[3]; + + if (count <= 0) + abort (); + if (rtx_code == ROTATE || rtx_code == ROTATERT) + abort (); + + /* Extract bit shifts over 16 bits by HImode moves. */ + if (count >= 16) + { + } + + /* Extract bit shifts over 8 bits by QImode moves. */ + if (count >= 8) + { + } + + /* Iterate over the number of bits to be shifted. */ + while (count > 0) + { + /* Each bit to be shifted requires 1 proper bit shift + and 3 rotates. */ + + /* First, do the arithmetic/logical shift. Left shifts + start from the LSB; right shifts start from the MSB. */ + xoperands[0] = adjust_address (operands[0], QImode, start); + switch (rtx_code) + { + case ASHIFT: + output_asm_insn ("asl\t%0", xoperands); + start--; + break; + case ASHIFTRT: + output_asm_insn ("asr\t%0", xoperands); + start++; + break; + case LSHIFTRT: + output_asm_insn ("lsr\t%0", xoperands); + start++; + break; + } + + /* Next, rotate the other bytes */ + for (s = start; s != end; s += step) + { + xoperands[0] = adjust_address (operands[0], QImode, s); + switch (rtx_code) + { + case ASHIFT: + output_asm_insn ("rol\t%0", xoperands); + break; + case ASHIFTRT: + case LSHIFTRT: + output_asm_insn ("ror\t%0", xoperands); + break; + } + } + count--; + } +} +#endif + +int +power_of_two_p (unsigned int n) +{ + return (n & (n-1)) == 0; +} + + +int +m6809_can_eliminate (int from, int to) +{ + if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return !frame_pointer_needed; + return 1; +} + + +int +m6809_initial_elimination_offset (int from, int to) +{ + switch (from) + { + case ARG_POINTER_REGNUM: + return get_frame_size () + m6809_get_regs_size (m6809_get_live_regs ()); + case FRAME_POINTER_REGNUM: + return get_frame_size (); + default: + gcc_unreachable (); + } +} + + +bool +m6809_frame_pointer_required (void) +{ + return false; +} + + +/* Defines the target-specific hooks structure. */ +struct gcc_target targetm = TARGET_INITIALIZER; diff -urN gcc-4.6.4-clean/gcc/config/m6809/m6809.h gcc-4.6.4/gcc/config/m6809/m6809.h --- gcc-4.6.4-clean/gcc/config/m6809/m6809.h 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/m6809.h 2017-11-28 21:17:43.127091341 -0700 @@ -0,0 +1,1336 @@ +/* Definitions of target machine for GNU compiler. MC6809 version. + + MC6809 Version by Tom Jones (jones@sal.wisc.edu) + Space Astronomy Laboratory + University of Wisconsin at Madison + + minor changes to adapt it to gcc-2.5.8 by Matthias Doerfel + ( msdoerfe@informatik.uni-erlangen.de ) + also added #pragma interrupt (inspired by gcc-6811) + + minor changes to adapt it to gcc-2.8.0 by Eric Botcazou + (ebotcazou@multimania.com) + + minor changes to adapt it to egcs-1.1.2 by Eric Botcazou + (ebotcazou@multimania.com) + + minor changes to adapt it to gcc-2.95.3 by Eric Botcazou + (ebotcazou@multimania.com) + + changes for gcc-3.1.1 by ??? + + further changes for gcc-3.1.1 and beyond by Brian Dominy + (brian@oddchange.com) + + even more changes for gcc-4.6.1 by William Astle (lost@l-w.ca) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +/* Helper macros for creating strings with macros */ +#define C_STRING(x) C_STR(x) +#define C_STR(x) #x + +/* Certain parts of GCC include host-side includes, which is bad. + * Some things that get pulled in need to be undone. + */ +#undef HAVE_GAS_HIDDEN + +/* Names to predefine in the preprocessor for this target machine. */ +/*#define TARGET_CPU_CPP_BUILTINS() m6809_cpu_cpp_builtins () */ +#define TARGET_CPU_CPP_BUILTINS() do \ + { \ + if (TARGET_6309) \ + { \ + builtin_define_std ("__M6309__"); \ + builtin_define_std ("__m6309__"); \ + } \ + else \ + { \ + builtin_define_std ("__M6809__"); \ + builtin_define_std ("__m6809__"); \ + } \ + \ + if (TARGET_BYTE_INT) \ + builtin_define_std ("__int8__"); \ + else \ + builtin_define_std ("__int16__"); \ + \ + switch (m6809_abi_version) \ + { \ + case M6809_ABI_VERSION_STACK: \ + builtin_define_std ("__regargs__"); \ + builtin_define_std ("__ABI_STACK__"); \ + break; \ + case M6809_ABI_VERSION_REGS: \ + builtin_define_std ("__ABI_REGS__"); \ + break; \ + case M6809_ABI_VERSION_BX: \ + builtin_define_std ("__ABI_BX__"); \ + break; \ + default: \ + break; \ + } \ + \ + if (TARGET_WPC) \ + builtin_define_std ("__WPC__"); \ + \ + if (TARGET_DRET) \ + builtin_define_std ("__DRET__"); \ + } while (0) + +/* As an embedded target, we have no libc. */ +#ifndef inhibit_libc +#define inhibit_libc +#endif + +/* Print subsidiary information on the compiler version in use. */ +#define TARGET_VERSION fprintf (stderr, " (MC6809)"); + +/* Run-time compilation parameters selecting different hardware subsets. */ +/*extern int target_flags; */ +extern short *reg_renumber; /* def in local_alloc.c */ + +/* Runtime current values of section names */ +extern int section_changed; +extern char code_section_op[], data_section_op[], bss_section_op[]; + +#define WARNING_OPT 0, +/*extern const char *m6809_abi_version_ptr; */ +extern unsigned int m6809_soft_regs; +extern unsigned int m6809_abi_version; + +/* ABI versions */ + +#define M6809_ABI_VERSION_STACK 0 +#define M6809_ABI_VERSION_REGS 1 +#define M6809_ABI_VERSION_BX 2 +#define M6809_ABI_VERSION_LATEST (M6809_ABI_VERSION_BX) + +/* Allow $ in identifiers */ +#define DOLLARS_IN_IDENTIFIERS 1 + +/*-------------------------------------------------------------- + Target machine storage layout +--------------------------------------------------------------*/ + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. */ +#define BITS_BIG_ENDIAN 0 + +/* Define to 1 if most significant byte of a word is the lowest numbered. */ +#define BYTES_BIG_ENDIAN 1 + +/* Define to 1 if most significant word of a multiword value is the lowest numbered. */ +#define WORDS_BIG_ENDIAN 1 + +/* Number of bits in an addressible storage unit */ +#define BITS_PER_UNIT 8 + +/* Width in bits of a "word", or the contents of a machine register. + * Although the 6809 has a few byte registers, define this to 16-bits + * since this is the natural size of most registers. */ +#define BITS_PER_WORD 16 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD (BITS_PER_WORD/8) + +/* Width in bits of a pointer. See also the macro `Pmode' defined below. */ +#define POINTER_SIZE 16 + +/* Allocation boundary (bits) for storing pointers in memory. */ +#define POINTER_BOUNDARY 8 + +/* Allocation boundary (bits) for storing arguments in argument list. */ +/* PARM_BOUNDARY is divided by BITS_PER_WORD in expr.c -- tej */ +#define PARM_BOUNDARY 8 + +/* Boundary (bits) on which stack pointer should be aligned. */ +#define STACK_BOUNDARY 8 + +/* Allocation boundary (bits) for the code of a function. */ +#define FUNCTION_BOUNDARY 8 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 8 + +/* Every structure's size must be a multiple of this. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* Largest mode size to use when putting an object, including + * a structure, into a register. By limiting this to 16, no + * 32-bit objects will ever be allocated to a pair of hard + * registers. This is a good thing, since there aren't that + * many of them. 32-bit objects are only needed for floats + * and "long long"s. Larger values have been tried and did not + * work. */ +#define MAX_FIXED_MODE_SIZE 16 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 8 + +/* Define this if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 0 + +/*-------------------------------------------------------------- + Standard register usage. +--------------------------------------------------------------*/ + +/* Register values as bitmasks. + * TODO : merge D_REGBIT and B_REGBIT, and treat this as the same + * register. */ +#define RSVD1_REGBIT (1 << HARD_RSVD1_REGNUM) +#define D_REGBIT (1 << HARD_D_REGNUM) +#define X_REGBIT (1 << HARD_X_REGNUM) +#define Y_REGBIT (1 << HARD_Y_REGNUM) +#define U_REGBIT (1 << HARD_U_REGNUM) +#define S_REGBIT (1 << HARD_S_REGNUM) +#define PC_REGBIT (1 << HARD_PC_REGNUM) +#define Z_REGBIT (1 << HARD_Z_REGNUM) +#define A_REGBIT (1 << HARD_A_REGNUM) +#define B_REGBIT (1 << HARD_B_REGNUM) +#define CC_REGBIT (1 << HARD_CC_REGNUM) +#define DP_REGBIT (1 << HARD_DP_REGNUM) +#define SOFT_FP_REGBIT (1 << SOFT_FP_REGNUM) +#define SOFT_AP_REGBIT (1 << SOFT_AP_REGNUM) +#define M_REGBIT(n) (1 << (SOFT_M0_REGNUM + n)) + +/* Macros for dealing with set of registers. + * A register set is just a bitwise-OR of all the register + * bitmask values. */ + +/* Which registers can hold 8-bits */ +#define BYTE_REGSET \ + (Z_REGBIT | A_REGBIT | D_REGBIT | CC_REGBIT | DP_REGBIT) + +/* Which registers can hold 16-bits. + * Note: D_REGBIT is defined as both an 8-bit and 16-bit register */ +#define WORD_REGSET \ + (D_REGBIT | X_REGBIT | Y_REGBIT | U_REGBIT | S_REGBIT | PC_REGBIT | SOFT_FP_REGBIT | SOFT_AP_REGBIT | RSVD1_REGBIT) + +/* Returns nonzero if a given REGNO is in the REGSET. */ +#define REGSET_CONTAINS_P(regno, regset) (((1 << (regno)) & (regset)) != 0) + +/* Defines related to the number of soft registers supported. + * The actual number used may be less depending on -msoft-reg-count. + * If you change one of these, you should change them all. */ +#define NUM_M_REGS 8 +#define M_REGS_FIXED 1, 1, 1, 1, 1, 1, 1, 1 +#define M_REGS_CALL_USED 1, 1, 1, 1, 1, 1, 1, 1 +#define HARD_M_REGNUMS \ + SOFT_M0_REGNUM+0, SOFT_M0_REGNUM+1, SOFT_M0_REGNUM+2, SOFT_M0_REGNUM+3, \ + SOFT_M0_REGNUM+4, SOFT_M0_REGNUM+5, SOFT_M0_REGNUM+6, SOFT_M0_REGNUM+7 + +#define SOFT_M_REGBITS (((1UL << NUM_M_REGS) - 1) << (SOFT_M0_REGNUM)) + +/* Number of actual hardware registers. + The hardware registers are assigned numbers for the compiler + from 0 to just below FIRST_PSEUDO_REGISTER. + All registers that the compiler knows about must be given numbers, + even those that are not normally considered general registers. + Make sure the constant below matches the value of SOFT_M0_REGNUM; + for some reason, GCC won't compile if that name is used here directly. */ +#ifdef SOFT_M0_REGNUM +#if (SOFT_M0_REGNUM != 14) +#error "bad register numbering" +#endif +#endif +#define FIRST_PSEUDO_REGISTER (14 + NUM_M_REGS) + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + The psuedoregisters (M_REGS) are declared fixed here, but + will be unfixed if -msoft-reg-count is seen later. */ +#define FIXED_REGISTERS \ + {1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, M_REGS_FIXED, } + /* -, X, Y, U, S, PC,D, Z, A, B, C, DP,FP,AP,M... */ + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. */ +#define CALL_USED_REGISTERS \ + {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, M_REGS_CALL_USED, } + /* -, X, Y, U, S, PC,D, Z, A, B, C, DP,FP,AP,M... */ + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + For the 6809, we distinguish between word-length and byte-length + registers. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (REGSET_CONTAINS_P (REGNO, WORD_REGSET) ? \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) : \ + (GET_MODE_SIZE (MODE))) + + +/* Value is 1 if hard register REGNO can hold a value +of machine-mode MODE. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) m6809_hard_regno_mode_ok (REGNO, MODE) + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) 0 + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* program counter if referenced as a register */ +#define PC_REGNUM HARD_PC_REGNUM + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM HARD_S_REGNUM + +/* Base register for access to local variables of the function. + * Before reload, FRAME_POINTER_REGNUM will be used. Later, + * the elimination pass will convert these to STACK_POINTER_REGNUM + * if possible, or else HARD_FRAME_POINTER_REGNUM. The idea is to + * avoid tying up a hard register (U) for the frame pointer if + * it can be eliminated entirely, making it available for use as + * a general register. */ +#define FRAME_POINTER_REGNUM SOFT_FP_REGNUM +#define HARD_FRAME_POINTER_REGNUM HARD_U_REGNUM + +/* Define a table of possible eliminations. + * The idea is to try to avoid using hard registers for the argument + * and frame pointers if they can be derived from the stack pointer + * instead, which already has a hard register reserved for it. + * + * The order of entries in this table will try to convert + * ARG_POINTER_REGNUM and FRAME_POINTER_REGNUM into stack pointer + * references first, but if that fails, they will be converted to use + * HARD_FRAME_POINTER_REGNUM. + */ +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }} + +/* #define CAN_ELIMINATE(FROM, TO) m6809_can_eliminate (FROM, TO) */ + +/* Define how to offset the frame or argument pointer to turn it + * into a stack pointer reference. This is based on the way that + * the frame is constructed in the function prologue. */ +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = m6809_initial_elimination_offset (FROM, TO) + +/* Base register for access to arguments of the function. + * This is only used prior to reload; no instructions will ever + * be output referring to this register. */ +#define ARG_POINTER_REGNUM SOFT_AP_REGNUM + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM HARD_Y_REGNUM + +/* #define CONDITIONAL_REGISTER_USAGE (m6809_conditional_register_usage ()) */ + +/* Order in which hard registers are allocated to pseudos. + * + * Since the D register is the only valid reg for 8-bit values + * now, avoid using it for 16-bit values by putting it after all + * other 16-bits. + * + * Prefer X first since the first 16-bit function argument goes + * there. We may be able to pass in to a subroutine without + * a copy. + * + * Prefer U over Y since instructions using Y take one extra + * byte, and thus one extra cycle to execute. + */ +#define REG_ALLOC_ORDER \ + { HARD_X_REGNUM, HARD_U_REGNUM, HARD_Y_REGNUM, HARD_D_REGNUM, \ + HARD_M_REGNUMS, HARD_S_REGNUM, HARD_PC_REGNUM, \ + HARD_B_REGNUM, HARD_A_REGNUM, HARD_CC_REGNUM, \ + HARD_DP_REGNUM, SOFT_FP_REGNUM, SOFT_AP_REGNUM, \ + 6, HARD_Z_REGNUM } + +/*-------------------------------------------------------------- + classes of registers +--------------------------------------------------------------*/ + +/* Define the classes of registers for register constraints in the + machine description. Also define ranges of constants. + + One of the classes must always be named ALL_REGS and include all hard regs. + If there is more than one class, another class must be named NO_REGS + and contain no registers. + + The name GENERAL_REGS must be the name of a class (or an alias for + another name such as ALL_REGS). This is the class of registers + that is allowed by "g" or "r" in a register constraint. + Also, registers outside this class are allocated only when + instructions express preferences for them. + + The classes must be numbered in nondecreasing order; that is, + a larger-numbered class must never be contained completely + in a smaller-numbered class. + + For any two classes, it is very desirable that there be another + class that represents their union. */ + +enum reg_class { + NO_REGS, /* The trivial class with no registers in it */ + D_REGS, /* 16-bit (word (HI)) data (D) */ + ACC_A_REGS, /* The A register */ + ACC_B_REGS, /* The B register */ + X_REGS, /* The X register */ + Z_REGS, /* The Z (zero-bit) register */ + Q_REGS, /* 8-bit (byte (QI)) data (A,B) */ + M_REGS, /* 8-bit (byte (QI)) soft registers */ + CC_REGS, /* 8-bit condition code register */ + I_REGS, /* An index register (A,B,D) */ + T_REGS, /* 16-bit addresses, not including stack or PC (X,Y,U) */ + A_REGS, /* 16-bit addresses (X,Y,U,S,PC) */ + S_REGS, /* 16-bit soft registers (FP, AP) */ + P_REGS, /* 16-bit pushable registers (D,X,Y,U); omit PC and S */ + G_REGS, /* 16-bit data and address (D,X,Y,U,S,PC) */ + ALL_REGS, /* All registers */ + LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Since GENERAL_REGS is a smaller class than ALL_REGS, + it is not an alias to ALL_REGS, but to G_REGS. */ +#define GENERAL_REGS G_REGS + +/* Give names of register classes as strings for dump file. */ +#define REG_CLASS_NAMES \ + { "NO_REGS", "D_REGS", "ACC_A_REGS", "ACC_B_REGS", "X_REGS", "Z_REGS", "Q_REGS", "M_REGS", \ + "CC_REGS", "I_REGS", "T_REGS", "A_REGS", "S_REGS", "P_REGS", "G_REGS", \ + "ALL_REGS" } + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. */ + +#define D_REGSET (D_REGBIT) +#define ACC_A_REGSET (A_REGBIT) +#define ACC_B_REGSET (D_REGBIT) +#define X_REGSET (X_REGBIT) +#define Z_REGSET (Z_REGBIT) +#define Q_REGSET (D_REGBIT | A_REGBIT) +#define M_REGSET (SOFT_M_REGBITS) +#define CC_REGSET (CC_REGBIT) +#define I_REGSET (A_REGBIT | B_REGBIT | D_REGBIT) +#define T_REGSET (X_REGBIT | Y_REGBIT | U_REGBIT) +#define A_REGSET (X_REGBIT | Y_REGBIT | U_REGBIT | S_REGBIT | PC_REGBIT) +#define S_REGSET (SOFT_FP_REGBIT | SOFT_AP_REGBIT) +#define P_REGSET (D_REGBIT | X_REGBIT | Y_REGBIT | U_REGBIT) +#define G_REGSET \ + (D_REGSET | Q_REGSET | I_REGSET | A_REGSET | M_REGSET | S_REGSET) +#define ALL_REGSET (G_REGSET) + +#define REG_CLASS_CONTENTS { \ + {0}, \ + {D_REGSET}, \ + {ACC_A_REGSET}, \ + {ACC_B_REGSET}, \ + {X_REGSET}, \ + {Z_REGSET}, \ + {Q_REGSET}, \ + {M_REGSET}, \ + {CC_REGSET}, \ + {I_REGSET}, \ + {T_REGSET}, \ + {A_REGSET}, \ + {S_REGSET}, \ + {P_REGSET}, \ + {G_REGSET}, \ + {ALL_REGSET}, \ +} + +/* The same information, inverted. + * This is defined to use the REG_CLASS_CONTENTS defines above, so that + * these two sets of definitions are always consistent. */ + +#define REGNO_REG_CLASS(REGNO) \ + (D_REGNO_P (REGNO) ? D_REGS : \ + (Z_REGNO_P (REGNO) ? Z_REGS : \ + (ACC_A_REGNO_P (REGNO) ? ACC_A_REGS : \ + (ACC_B_REGNO_P (REGNO) ? ACC_B_REGS : \ + (X_REGNO_P (REGNO) ? X_REGS : \ + (Q_REGNO_P (REGNO) ? Q_REGS : \ + (M_REGNO_P (REGNO) ? M_REGS : \ + (CC_REGNO_P (REGNO) ? CC_REGS : \ + (I_REGNO_P (REGNO) ? I_REGS : \ + (T_REGNO_P (REGNO) ? T_REGS : \ + (A_REGNO_P (REGNO) ? A_REGS : \ + (S_REGNO_P (REGNO) ? S_REGS : \ + (P_REGNO_P (REGNO) ? P_REGS : \ + (G_REGNO_P (REGNO) ? G_REGS : ALL_REGS)))))))))))))) + +#define D_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, D_REGSET)) +#define ACC_A_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, ACC_A_REGSET)) +#define ACC_B_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, ACC_B_REGSET)) +#define X_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, X_REGSET)) +#define Z_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, Z_REGSET)) +#define Q_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, Q_REGSET)) +#define M_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, M_REGSET)) +#define CC_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, CC_REGSET)) +#define I_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, I_REGSET)) +#define T_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, T_REGSET)) +#define A_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, A_REGSET)) +#define S_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, S_REGSET)) +#define P_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, P_REGSET)) +#define G_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, G_REGSET)) + +/* Macros that test an rtx 'X' to see if it's in a particular + * register class. 'X' need not be a REG necessarily. */ + +#define D_REG_P(X) (REG_P (X) && D_REGNO_P (REGNO (X))) +#define ACC_A_REG_P(X) (REG_P (X) && ACC_A_REGNO_P (REGNO (X))) +#define ACC_B_REG_P(X) (REG_P (X) && ACC_B_REGNO_P (REGNO (X))) +#define X_REG_P(X) (REG_P (X) && X_REGNO_P (REGNO (X))) +#define Z_REG_P(X) (REG_P (X) && Z_REGNO_P (REGNO (X))) +#define I_REG_P(X) (REG_P (X) && I_REGNO_P (REGNO (X))) +#define T_REG_P(X) (REG_P (X) && T_REGNO_P (REGNO (X))) +#define A_REG_P(X) (REG_P (X) && A_REGNO_P (REGNO (X))) +#define S_REG_P(X) (REG_P (X) && S_REGNO_P (REGNO (X))) +#define P_REG_P(X) (REG_P (X) && P_REGNO_P (REGNO (X))) +#define Q_REG_P(X) (REG_P (X) && Q_REGNO_P (REGNO (X))) +#define M_REG_P(X) (REG_P (X) && M_REGNO_P (REGNO (X))) +#define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X))) + +/* Redefine this in terms of BYTE_REGSET */ +#define BYTE_REGNO_P(REGNO) (REGSET_CONTAINS_P (REGNO, BYTE_REGSET)) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS I_REGS +#define BASE_REG_CLASS A_REGS + +/* Get reg_class from a letter in the machine description. */ +#define REG_CLASS_FROM_LETTER(C) \ + (((C) == 'a' ? A_REGS : \ + ((C) == 'd' ? D_REGS : \ + ((C) == 'x' ? I_REGS : \ + ((C) == 't' ? M_REGS : \ + ((C) == 'c' ? CC_REGS : \ + ((C) == 'A' ? ACC_A_REGS : \ + ((C) == 'B' ? ACC_B_REGS : \ + ((C) == 'v' ? X_REGS : \ + ((C) == 'u' ? S_REGS : \ + ((C) == 'U' ? P_REGS : \ + ((C) == 'T' ? T_REGS : \ + ((C) == 'z' ? Z_REGS : \ + ((C) == 'q' ? Q_REGS : NO_REGS)))))))))))))) + +/*-------------------------------------------------------------- + The letters I through O in a register constraint string + can be used to stand for particular ranges of immediate operands. + This macro defines what the ranges are. + C is the letter, and VALUE is a constant value. + Return 1 if VALUE is in the range specified by C. + + For the 6809, J, K, L are used for indexed addressing. + `I' is used for the constant 1. + `J' is used for the 5-bit offsets. + `K' is used for the 8-bit offsets. + `L' is used for the range of signed numbers that fit in 16 bits. + `M' is used for the exact value '8'. + `N' is used for the constant -1. + `O' is used for the constant 0. +--------------------------------------------------------------*/ + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? ((VALUE) == 1) : \ + (C) == 'J' ? ((VALUE) >= -16 && (VALUE) <= 15) : \ + (C) == 'K' ? ((VALUE) >= -128 && (VALUE) <= 127) : \ + (C) == 'L' ? ((VALUE) >= -32768 && (VALUE) <= 32767) : \ + (C) == 'M' ? ((VALUE) == 8) : \ + (C) == 'N' ? ((VALUE) == -1) : \ + (C) == 'O' ? ((VALUE) == 0) : 0) + +/* Similar, but for floating constants, and defining letters G and H. + No floating-point constants are valid on MC6809. */ +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' ? (GET_MODE_CLASS (GET_MODE (VALUE)) == MODE_FLOAT \ + && VALUE == CONST0_RTX (GET_MODE (VALUE))) : 0) + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS; but on some machines + in some cases it is preferable to use a more restrictive class. */ +#define PREFERRED_RELOAD_CLASS(X,CLASS) m6809_preferred_reload_class(X,CLASS) + +#define SMALL_REGISTER_CLASSES 1 + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/*-------------------------------------------------------------- + Stack layout; function entry, exit and calling. +--------------------------------------------------------------*/ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_DOWNWARD + + +/* Define this if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +#define FRAME_GROWS_DOWNWARD 1 + + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET 0 + + +/* Always push stack arguments for now. Accumulation is not yet working. */ +#define PUSH_ROUNDING(BYTES) (BYTES) + + +/* Offset of first parameter from the argument pointer register value. + * ARG_POINTER_REGNUM is defined to point to the return address pushed + * onto the stack, so we must offset by 2 bytes to get to the arguments. */ +#define FIRST_PARM_OFFSET(FNDECL) 2 + +/* Value is 1 if returning from a function call automatically + pops the arguments described by the number-of-args field in the call. + FUNTYPE is the data type of the function (as a tree), + or for a library call it is an identifier node for the subroutine name. */ +/* #define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 */ + +/* Define how to find the value returned by a function. + VALTYPE is the data type of the value (as a tree). + If the precise function being called is known, FUNC is its FUNCTION_DECL; + otherwise, FUNC is 0. */ +#define FUNCTION_VALUE(VALTYPE, FUNC) m6809_function_value (VALTYPE, FUNC) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ + +/* All return values are in the X-register. */ +#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, HARD_X_REGNUM) + +/* Define this if using the nonreentrant convention for returning + structure and union values. No; it is inefficient and buggy. */ +#undef PCC_STATIC_STRUCT_RETURN + +/* 1 if N is a possible register number for a function value. */ +#define FUNCTION_VALUE_REGNO_P(N) m6809_function_value_regno_p (N) + +/* Define this to be true when FUNCTION_VALUE_REGNO_P is true for + more than one register. */ +#define NEEDS_UNTYPED_CALL 1 + +/* 1 if N is a possible register number for function argument passing. */ +#define FUNCTION_ARG_REGNO_P(N) \ + ((m6809_abi_version != M6809_ABI_VERSION_STACK) ? \ + (((N) == HARD_D_REGNUM) || ((N) == HARD_X_REGNUM)) : \ + 0) + +/*-------------------------------------------------------------- + Argument Lists +--------------------------------------------------------------*/ + +/* Cumulative arguments are tracked in a single integer, + * which is the number of bytes of arguments scanned so far, + * plus which registers have already been used. The register + * info is kept in some of the upper bits */ +#define CUMULATIVE_ARGS unsigned int + +#define CUM_STACK_ONLY 0x80000000 +#define CUM_X_MASK 0x40000000 +#define CUM_B_MASK 0x20000000 +#define CUM_STACK_INVALID 0x10000000 +#define CUM_STACK_MASK 0xFFFFFFF + +#define CUM_ADVANCE_8BIT(cum) \ + (((cum) & CUM_B_MASK) ? (cum)++ : ((cum) |= CUM_B_MASK)) + +#define CUM_ADVANCE_16BIT(cum) \ + (((cum) & CUM_X_MASK) ? (cum) += 2 : ((cum) |= CUM_X_MASK)) + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + N_NAMED was added in gcc 3.4 and is not used currently. */ +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT,N_NAMED) \ + ((CUM) = m6809_init_cumulative_args (CUM, FNTYPE, LIBNAME)) + +#define FUNCTION_ARG_SIZE(MODE, TYPE) \ + ((MODE) != BLKmode ? GET_MODE_SIZE (MODE) \ + : (unsigned) int_size_in_bytes (TYPE)) + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + (((MODE == QImode) && !((CUM) & CUM_STACK_ONLY)) ? \ + CUM_ADVANCE_8BIT (CUM) : \ + ((MODE == HImode) && !((CUM) & CUM_STACK_ONLY)) ? \ + CUM_ADVANCE_16BIT (CUM) : \ + ((CUM) = ((CUM) + (TYPE ? int_size_in_bytes (TYPE) : 2)))) + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register rtx in which to store the argument. + This macro is used _before_ FUNCTION_ARG_ADVANCE. + + For the 6809, the first 8-bit function argument can be placed into B, + and the first 16-bit arg can go into X. All other arguments + will be pushed onto the stack. + + Command-line options can adjust this behavior somewhat. + */ +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + ((MODE == VOIDmode) ? NULL_RTX : \ + ((MODE == BLKmode) || (GET_MODE_SIZE (MODE) > 2)) ? NULL_RTX : \ + ((MODE == QImode) && !((CUM) & (CUM_STACK_ONLY | CUM_B_MASK))) ? \ + gen_rtx_REG (QImode, HARD_D_REGNUM) : \ + ((MODE == HImode) && !((CUM) & (CUM_STACK_ONLY | CUM_X_MASK))) ? \ + gen_rtx_REG (HImode, HARD_X_REGNUM) : m6809_function_arg_on_stack (&CUM)) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tldd\t#LP%u\n\tjsr\tmcount\n", (LABELNO)); + +/* Stack pointer must be correct on function exit */ +#define EXIT_IGNORE_STACK 0 + +/***************************************************************************** +** +** Trampolines for Nested Functions +** +*****************************************************************************/ + +/* Length in units of the trampoline for entering a nested function. */ +#define TRAMPOLINE_SIZE 7 + +/*-------------------------------------------------------------- + Addressing modes, + and classification of registers for them. +--------------------------------------------------------------*/ + +/* 6809 has postincrement and predecrement addressing modes */ +#define HAVE_POST_INCREMENT 1 +#define HAVE_PRE_DECREMENT 1 + +/* Whether or not to use index registers is configurable. + * Experiments show that things work better when this is off, so + * that's the way it is for now. */ +#undef USE_INDEX_REGISTERS + + +/* Macros to check register numbers against specific register classes. */ +#define REG_VALID_FOR_BASE_P(REGNO) \ + (((REGNO) < FIRST_PSEUDO_REGISTER) && A_REGNO_P (REGNO)) + +/* MC6809 index registers do not allow scaling, */ +/* but there is "accumulator-offset" mode. */ +#ifdef USE_INDEX_REGISTERS +#define REG_VALID_FOR_INDEX_P(REGNO) \ + (((REGNO) < FIRST_PSEUDO_REGISTER) && I_REGNO_P (REGNO)) +#else +#define REG_VALID_FOR_INDEX_P(REGNO) 0 +#endif + +/* Internal macro, the nonstrict definition for REGNO_OK_FOR_BASE_P */ +#define REGNO_OK_FOR_BASE_NONSTRICT_P(REGNO) \ + ((REGNO) >= FIRST_PSEUDO_REGISTER \ + || REG_VALID_FOR_BASE_P (REGNO) \ + || (REGNO) == FRAME_POINTER_REGNUM \ + || (REGNO) == HARD_FRAME_POINTER_REGNUM \ + || (REGNO) == ARG_POINTER_REGNUM \ + || (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO]))) + +/* Internal macro, the nonstrict definition for REGNO_OK_FOR_INDEX_P */ +#define REGNO_OK_FOR_INDEX_NONSTRICT_P(REGNO) \ + ((REGNO) >= FIRST_PSEUDO_REGISTER \ + || REG_VALID_FOR_INDEX_P (REGNO) \ + || (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))) + + +/* Internal macro, the strict definition for REGNO_OK_FOR_BASE_P */ +#define REGNO_OK_FOR_BASE_STRICT_P(REGNO) \ + ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_BASE_P (REGNO) \ + : (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO]))) + + +/* Internal macro, the strict definition for REGNO_OK_FOR_INDEX_P */ +#define REGNO_OK_FOR_INDEX_STRICT_P(REGNO) \ + ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_INDEX_P (REGNO) \ + : (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))) + + +#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_BASE_STRICT_P (REGNO) + +#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_INDEX_STRICT_P (REGNO) + +#define REG_OK_FOR_BASE_STRICT_P(X) REGNO_OK_FOR_BASE_STRICT_P (REGNO (X)) +#define REG_OK_FOR_BASE_NONSTRICT_P(X) REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO (X)) +#define REG_OK_FOR_INDEX_STRICT_P(X) REGNO_OK_FOR_INDEX_STRICT_P (REGNO (X)) +#define REG_OK_FOR_INDEX_NONSTRICT_P(X) REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO (X)) + +#ifndef REG_OK_STRICT +#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P(X) +#ifdef USE_INDEX_REGISTERS +#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P(X) +#else +#define REG_OK_FOR_INDEX_P(X) 0 +#endif +#else +#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P (X) +#ifdef USE_INDEX_REGISTERS +#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_STRICT_P (X) +#else +#define REG_OK_FOR_INDEX_P(X) 0 +#endif +#endif + +/* Maximum number of registers that can appear in a valid memory address */ +#ifdef USE_INDEX_REGISTERS +#define MAX_REGS_PER_ADDRESS 2 +#else +#define MAX_REGS_PER_ADDRESS 1 +#endif + +/* 1 if X is an rtx for a constant that is a valid address. + * We allow any constant, plus the sum of any two constants (this allows + * offsetting a symbol ref) */ +#define CONSTANT_ADDRESS_P(X) \ + ((CONSTANT_P (X)) \ + || ((GET_CODE (X) == PLUS) \ + && (CONSTANT_P (XEXP (X, 0))) && (CONSTANT_P (XEXP (X, 1))))) + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ +/* Any single-word constant is ok; the only contexts + allowing general_operand of mode DI or DF are movdi and movdf. */ +#define LEGITIMATE_CONSTANT_P(X) (GET_CODE (X) != CONST_DOUBLE) + +/* Nonzero if the X is a legitimate immediate operand in PIC mode. */ +#define LEGITIMATE_PIC_OPERAND_P(X) !symbolic_operand (X, VOIDmode) + +/*-------------------------------------------------------------- + Test for valid memory addresses +--------------------------------------------------------------*/ +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. */ + +/*-------------------------------------------------------------- + Valid addresses are either direct or indirect (MEM) versions + of the following forms. + constant N + register ,X + constant indexed N,X + accumulator indexed D,X + auto_increment ,X++ + auto_decrement ,--X +--------------------------------------------------------------*/ + +#define REGISTER_ADDRESS_P(X) \ + (REG_P (X) && REG_OK_FOR_BASE_P (X)) + +#define EXTENDED_ADDRESS_P(X) \ + CONSTANT_ADDRESS_P (X) \ + +#define LEGITIMATE_BASE_P(X) \ + ((REG_P (X) && REG_OK_FOR_BASE_P (X)) \ + || (GET_CODE (X) == SIGN_EXTEND \ + && GET_CODE (XEXP (X, 0)) == REG \ + && GET_MODE (XEXP (X, 0)) == HImode \ + && REG_OK_FOR_BASE_P (XEXP (X, 0)))) + +#define LEGITIMATE_OFFSET_P(X) \ + (CONSTANT_ADDRESS_P (X) || (REG_P (X) && REG_OK_FOR_INDEX_P (X))) + +/* 1 if X is the sum of a base register and an offset. */ +#define INDEXED_ADDRESS(X) \ + ((GET_CODE (X) == PLUS \ + && LEGITIMATE_BASE_P (XEXP (X, 0)) \ + && LEGITIMATE_OFFSET_P (XEXP (X, 1))) \ + || (GET_CODE (X) == PLUS \ + && LEGITIMATE_BASE_P (XEXP (X, 1)) \ + && LEGITIMATE_OFFSET_P (XEXP (X, 0)))) + +#define STACK_REG_P(X) (REG_P(X) && REGNO(X) == HARD_S_REGNUM) + +#define STACK_PUSH_P(X) \ + (MEM_P (X) && GET_CODE (XEXP (X, 0)) == PRE_DEC && STACK_REG_P (XEXP (XEXP (X, 0), 0))) + +#define STACK_POP_P(X) \ + (MEM_P (X) && GET_CODE (XEXP (X, 0)) == POST_INC && STACK_REG_P (XEXP (XEXP (X, 0), 0))) + +#define PUSH_POP_ADDRESS_P(X) \ + (((GET_CODE (X) == PRE_DEC) || (GET_CODE (X) == POST_INC)) \ + && (LEGITIMATE_BASE_P (XEXP (X, 0)))) + +/* Go to ADDR if X is a valid address. */ +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if (REGISTER_ADDRESS_P(X)) goto ADDR; \ + if (PUSH_POP_ADDRESS_P (X)) goto ADDR; \ + if (EXTENDED_ADDRESS_P (X)) goto ADDR; \ + if (INDEXED_ADDRESS (X)) goto ADDR; \ + if (MEM_P (X) && REGISTER_ADDRESS_P(XEXP (X, 0))) goto ADDR; \ + if (MEM_P (X) && PUSH_POP_ADDRESS_P (XEXP (X, 0))) goto ADDR; \ + if (MEM_P (X) && EXTENDED_ADDRESS_P (XEXP (X, 0))) goto ADDR; \ + if (MEM_P (X) && INDEXED_ADDRESS (XEXP (X, 0))) goto ADDR; \ +} + +/*-------------------------------------------------------------- + Address Fix-up +--------------------------------------------------------------*/ +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. + In the latest GCC, this case is already handled by the core code + so no action is required here. */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {} + + +/*-------------------------------------------------------------- + Miscellaneous Parameters +--------------------------------------------------------------*/ +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE Pmode + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#define DEFAULT_SIGNED_CHAR 0 + +/* This flag, if defined, says the same insns that convert to a signed fixnum + also convert validly to an unsigned one. */ +#define FIXUNS_TRUNC_LIKE_FIX_TRUNC + +/* Max number of bytes we can move from memory to memory/register + in one reasonably fast instruction. */ +#define MOVE_MAX 2 + +/* Int can be 8 or 16 bits (default is 16) */ +#define INT_TYPE_SIZE (TARGET_BYTE_INT ? 8 : 16) + +/* Short is always 16 bits */ +#define SHORT_TYPE_SIZE (TARGET_BYTE_INT ? 8 : 16) + +/* Size (bits) of the type "long" on target machine */ +#define LONG_TYPE_SIZE (TARGET_BYTE_INT ? 16 : 32) + +/* Size (bits) of the type "long long" on target machine */ +#define LONG_LONG_TYPE_SIZE 32 + +/* Size (bits) of the type "char" on target machine */ +#define CHAR_TYPE_SIZE 8 + +/* Size (bits) of the type "float" on target machine */ +#define FLOAT_TYPE_SIZE 32 + +/* Size (bits) of the type "double" on target machine. + * Note that the C standard does not require that doubles + * hold any more bits than float. Since the 6809 has so few + * registers, we cannot really support more than 32-bits. */ +#define DOUBLE_TYPE_SIZE 32 + +/* Size (bits) of the type "long double" on target machine */ +#define LONG_DOUBLE_TYPE_SIZE 32 + +/* Define the type used for "size_t". With a 64KB address space, + * only a 16-bit value here makes sense. */ +#define SIZE_TYPE (TARGET_BYTE_INT ? "long unsigned int" : "unsigned int") + +/* Likewise, the difference between two pointers is also a 16-bit + * signed value. */ +#define PTRDIFF_TYPE (TARGET_BYTE_INT ? "long int" : "int") + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 0 + +/* Define if shifts truncate the shift count + which implies one can omit a sign-extension or zero-extension + of a shift count. */ +#define SHIFT_COUNT_TRUNCATED 0 + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* It is as good to call a constant function address as to + call an address kept in a register. */ +#define NO_FUNCTION_CSE + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode HImode + +/* A function address in a call instruction + is a byte address (for indexing purposes) + so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE HImode + +/* Define the cost of moving a value from a register in CLASS1 + * to CLASS2, of a given MODE. + * + * On the 6809, hard register transfers are all basically equivalent. + * But soft register moves are treated more like memory moves. */ +#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \ + (((CLASS1 == M_REGS) || (CLASS2 == M_REGS)) ? 4 : 7) + +/* Define the cost of moving a value between a register and memory. */ +#define MEMORY_MOVE_COST(MODE, CLASS, IN) 5 + +/* Check a `double' value for validity for a particular machine mode. */ + +#define CHECK_FLOAT_VALUE(MODE, D, OVERFLOW) \ + ((OVERFLOW) = check_float_value (MODE, &D, OVERFLOW)) + + +/*-------------------------------------------------------------- + machine-dependent +--------------------------------------------------------------*/ +/* Tell final.c how to eliminate redundant test instructions. */ + +/* Here we define machine-dependent flags and fields in cc_status + (see `conditions.h'). */ + +/* Store in cc_status the expressions + that the condition codes will describe + after execution of an instruction whose pattern is EXP. + Do not alter them if the instruction would not alter the cc's. */ + +/* On the 6809, most of the insns to store in an address register + fail to set the cc's. However, in some cases these instructions + can make it possibly invalid to use the saved cc's. In those + cases we clear out some or all of the saved cc's so they won't be used. */ + +#define NOTICE_UPDATE_CC(EXP, INSN) \ + notice_update_cc((EXP), (INSN)) + +/***************************************************************************** +** +** pragma support +** +*****************************************************************************/ + +#if 0 +#define REGISTER_TARGET_PRAGMAS() \ +do { \ + extern void pragma_section PARAMS ((cpp_reader *)); \ + c_register_pragma (0, "section", pragma_section); \ +} while (0) + +#endif + +/*-------------------------------------------------------------- + ASSEMBLER FORMAT +--------------------------------------------------------------*/ + +#define FMT_HOST_WIDE_INT "%ld" + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ +#define ASM_APP_ON ";----- asm -----\n" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ +#define ASM_APP_OFF ";--- end asm ---\n" + +/* Use a semicolon to begin a comment. */ +#define ASM_COMMENT_START "; " + +/* Output assembly directives to switch to section 'name' */ +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION m6809_asm_named_section + +#undef TARGET_HAVE_NAMED_SECTION +#define TARGET_HAVE_NAMED_SECTION m6809_have_named_section + +/* Output before read-only data. */ +#define TEXT_SECTION_ASM_OP (code_section_op) + +/* Output before writable data. */ +#define DATA_SECTION_ASM_OP (data_section_op) + +/* Output before uninitialized data. */ +#define BSS_SECTION_ASM_OP (bss_section_op) + +/* Support the ctors and dtors sections for g++. */ + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP "\t.area .ctors" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP "\t.area .dtors" + + +#undef DO_GLOBAL_CTORS_BODY +#undef DO_GLOBAL_DTORS_BODY + +#define HAS_INIT_SECTION + +/* This is how to output an assembler line + that says to advance the location counter + to a multiple of 2**LOG bytes. */ + +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + if ((LOG) > 1) \ + fprintf (FILE, "\t.bndry %u\n", 1 << (LOG)) + +/* The .set foo,bar construct doesn't work by default */ +#undef SET_ASM_OP +#define ASM_OUTPUT_DEF(FILE, LABEL1, LABEL2) \ + do { \ + assemble_name (FILE, LABEL1); \ + fputs ("\tequ\t", 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. */ +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + do { \ + fprintf (FILE, "\tzmb\t%d\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.4-clean/gcc/config/m6809/m6809.md gcc-4.6.4/gcc/config/m6809/m6809.md --- gcc-4.6.4-clean/gcc/config/m6809/m6809.md 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/m6809.md 2017-11-28 21:12:11.156911596 -0700 @@ -0,0 +1,2358 @@ +;; GCC machine description for Motorola 6809 +;; Copyright (C) 1989, 2005, 2006, 2007, 2008, +;; 2009 Free Software Foundation, Inc. +;; +;; Mostly by Brian Dominy (brian@oddchange.com) with substantial renovations +;; by William Astle (lost@l-w.ca). +;; +;; Based on earlier work by Tom Jones (jones@sal.wisc.edu) and +;; Matthias Doerfel (msdoerfe@informatik.uni-erlangen.de) +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. +;; +;; General information: +;; -------------------- +;; * This backend is mostly a rewrite from earlier (3.1.1 and before) +;; versions. +;; +;; * The 'A' and 'B' registers are treated as a single register by the +;; register allocator; hence, the instruction templates assume that +;; both can be modified if either one is available for use. No +;; attempt is made to split instructions to refer to a particular half +;; of the register. It is always referred to as the 'D' register, even +;; in QImode (when it will be displayed as 'B'). +;; +;; * There is full support for proper branch instruction generation, +;; based on instruction lengths. However, many instruction patterns +;; are still overloaded to emit lots of real instructions, which can +;; make the length calculation difficult; in those cases, I've tried +;; to be pessimistic and assume the worst-case. +;; +;; * The instruction type attributes are only defined for branch +;; vs. non branch instructions for now, since there is seemingly no +;; reason to define these for other types anyway. +;; +;; * The limited number of total registers presents the greatest +;; challenge. There are 'soft registers' -- memory locations +;; used to simulate real regs -- which can be helpful. +;; +;; * Position-independent code (PIC) is supported and has been tested +;; but not to the extent of absolute code generation. +;; +;; * All of the 6809 special opcodes, e.g. SWI and SYNC, are defined +;; as UNSPEC instructions, and can be accessed from C code using +;; __builtin_xxxx() style functions. +;; +;; What still needs to be done: +;; ---------------------------- +;; * Replace remaining instances of (define_peephole) with +;; (define_peephole2), or remove them completely if they are not +;; matching anyway. Add more peepholes for things actually encountered. +;; +;; * Indexing addressing can lead to crashes in complex functions when +;; register pressure is high. Only the 'D' register can actually be +;; used as an index register, and its demand by other instructions +;; can sometimes mean that it is impossible to satisfy constraints. +;; Currently, indexing is completely disabled to avoid these types +;; of problems, although code is slightly more inefficient in some +;; working cases. +;; +;; * 32-bit math is terribly inefficient. +;; + + +;;-------------------------------------------------------------------- +;;- Constants +;;-------------------------------------------------------------------- + +; +; Define constants for hard register numbers. +; +(define_constants [ + (HARD_RSVD1_REGNUM 0) + (HARD_X_REGNUM 1) (HARD_Y_REGNUM 2) (HARD_U_REGNUM 3) + (HARD_S_REGNUM 4) (HARD_PC_REGNUM 5) (HARD_D_REGNUM 6) + (HARD_Z_REGNUM 7) + (HARD_A_REGNUM 8) (HARD_B_REGNUM 9) + (HARD_CC_REGNUM 10) (HARD_DP_REGNUM 11) + (SOFT_FP_REGNUM 12) (SOFT_AP_REGNUM 13) + (SOFT_M0_REGNUM 14) (SOFT_M1_REGNUM 15) + (SOFT_M2_REGNUM 16) (SOFT_M3_REGNUM 17) +]) + + +; +; The range in which a short branch insn can be used. +; +(define_constants [ + (MIN_SHORT_BRANCH_OFFSET -127) + (MAX_SHORT_BRANCH_OFFSET 128) +]) + + +; +; The lengths of various types of real 6809 instructions. +; +; By default, ordinary insns are 4 bytes long. This is often not +; right, and the insn patterns below will redefine this to the +; correct value. +; +; Branch instruction lengths (conditional and unconditionals) are +; well known and declared here. The short insns are used when the +; offset is within the range declared above (between MIN_SHORT +; and MAX_SHORT) ; otherwise the long form is used. +; +(define_constants [ + (DEFAULT_INSN_LENGTH 4) + (SHORT_CBRANCH_LENGTH 2) + (LONG_CBRANCH_LENGTH 4) + (SHORT_BRANCH_LENGTH 2) + (LONG_BRANCH_LENGTH 3) +]) + + +; +; Constants for insn cycle counts. +; Note that these counts all assume 1-byte opcodes. 2-byte +; opcodes require 1 extra cycles for fetching the extra byte. +; +(define_constants [ + ;; The default insn length, when it cannot be calculated. + ;; Take a conservative approach and estimate high. + (DEFAULT_INSN_CYCLES 10) + + ;; Cycle counts for ALU and load operations. + (ALU_INHERENT_CYCLES 2) + (ALU_IMMED_CYCLES 2) + (ALU_DIRECT_CYCLES 4) + (ALU_INDEXED_BASE_CYCLES 4) + (ALU_EXTENDED_CYCLES 5) + + ;; If an ALU operation is on a 16-bit register (D), then + ;; add this number of cycles to the total count. + (ALU_16BIT_CYCLES 2) + + ;; A load of a 16-bit register incurs this extra amount. + (LOAD_16BIT_CYCLES 1) + + ;; Cycle counts for memory-only operations (bit shifts, clear, test) + (MEM_DIRECT_CYCLES 6) + (MEM_INDEXED_BASE_CYCLES 6) + (MEM_EXTENDED_CYCLES 7) + + ;; Cycle count for any reg-reg transfer (regardless of size) + (EXG_CYCLES 8) + (TFR_CYCLES 6) + + ;; Cycle count for a condition code update (andcc/orcc) + (CC_CYCLES 3) + + (JMP_DIRECT_CYCLES 3) + (JMP_INDEXED_BASE_CYCLES 3) + (JMP_EXTENDED_CYCLES 4) + + (JSR_DIRECT_CYCLES 7) + (JSR_INDEXED_BASE_CYCLES 7) + (JSR_EXTENDED_CYCLES 8) + + (LEA_BASE_CYCLES 4) + + ;; Cycle count for a psh/pul operations. Add to this the + ;; total number of bytes moved for the correct count. + (PSH_PUL_CYCLES 5) + + ;; Miscellaneous cycle counts + (CWAI_CYCLES 20) + (MUL_CYCLES 11) + (NOP_CYCLES 2) + (RTI_CYCLES 15) + (RTS_CYCLES 5) + (SWI_CYCLES 20) + (SYNC_CYCLES 4) +]) + + +; +; An enumeration of values for each "unspec"; i.e. unspecified +; instruction. These represent insns that are meaningful on the +; 6809 but which have no intrinsic meaning to GCC itself. +; These insns can be generated explicitly using the __builtin_xxx +; syntax; they are also implicitly generated by the backend +; as needed to implement other insns. +; +(define_constants [ + (UNSPEC_BLOCKAGE 0) + (UNSPEC_PUSH_RS 1) + (UNSPEC_POP_RS 2) + (UNSPEC_SWI 3) + (UNSPEC_CWAI 4) + (UNSPEC_ADD_CARRY 5) + (UNSPEC_SUB_CARRY 6) + (UNSPEC_SYNC 7) + (UNSPEC_ADD_DECIMAL 8) +]) + + +;;-------------------------------------------------------------------- +;;- Predicates +;;-------------------------------------------------------------------- + +(include "predicates.md") + +;;-------------------------------------------------------------------- +;;- Attributes +;;-------------------------------------------------------------------- + +;; +;; The type attribute is used to distinguish between different +;; types of branch instructions, so that their lengths can be +;; calculated correctly. +;; +(define_attr "type" "branch,cbranch,unknown" (const_string "unknown")) + +;; +;; The length of a branch instruction is calculated based on how +;; far away the branch target is. Lengths of other insns default +;; to 4. set_attr is used in instruction templates to specify +;; the length when it is known exactly. When not sure, err on +;; the high side to avoid compile errors. +;; +(define_attr "length" "" + (cond [ + (eq_attr "type" "branch") + (if_then_else (lt (minus (match_dup 0) (pc)) + (const_int MIN_SHORT_BRANCH_OFFSET)) + (const_int LONG_BRANCH_LENGTH) + (if_then_else (gt (minus (match_dup 0) (pc)) + (const_int MAX_SHORT_BRANCH_OFFSET)) + (const_int LONG_BRANCH_LENGTH) + (const_int SHORT_BRANCH_LENGTH))) + (eq_attr "type" "cbranch") + (if_then_else (lt (minus (match_dup 0) (pc)) + (const_int MIN_SHORT_BRANCH_OFFSET)) + (const_int LONG_CBRANCH_LENGTH) + (if_then_else (gt (minus (match_dup 0) (pc)) + (const_int MAX_SHORT_BRANCH_OFFSET)) + (const_int LONG_CBRANCH_LENGTH) + (const_int SHORT_CBRANCH_LENGTH))) + ] (const_int DEFAULT_INSN_LENGTH))) + + +;; +;; The default attributes for 'asm' statements. +;; The default length is the longest possible single 6809 instruction, +;; which is 5 bytes. GCC will automatically multiply this by the +;; number of real insns contained in an asm statement. +;; +(define_asm_attributes + [(set_attr "length" "5") + (set_attr "type" "unknown")]) + +;; +;; An attribute for the number of cycles that it takes an instruction +;; to execute. +;; +(define_attr "cycles" "" (const_int DEFAULT_INSN_CYCLES)) + + +;;-------------------------------------------------------------------- +;;- Instruction patterns. When multiple patterns apply, +;;- the first one in the file is chosen. +;;- +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. +;;- +;;- Note: NOTICE_UPDATE_CC in m6809.h handles condition code updates +;;- for most instructions. +;;-------------------------------------------------------------------- + +;;-------------------------------------------------------------------- +;;- Test +;;-------------------------------------------------------------------- + +;; cmpx is 3 bytes, not 4 +(define_insn "*tsthi_x" + [(set (cc0) (match_operand:HI 0 "register_operand_x" "v"))] + "" + "cmpx\t#0" + [(set_attr "length" "3")]) + +;; subd #0 is 3 bytes, better than cmpd #0 which is 4 bytes +(define_insn "*tsthi_d" + [(set (cc0) (match_operand:HI 0 "register_operand_d" "d"))] + "" + "subd\t#0" + [(set_attr "length" "3")]) + +(define_insn "*tsthi" + [(set (cc0) (match_operand:HI 0 "register_operand" "a"))] + "" + "cmp%0\t#0" + [(set_attr "length" "4")]) + +(define_insn "*bitqi3" + [(set (cc0) + (and:QI (match_operand:QI 0 "register_operand" "%q") + (match_operand:QI 1 "general_operand" "mi")))] + "" + "bit%0\t%1" + [(set_attr "length" "3")]) + + +(define_insn "tstqi" + [(set (cc0) (match_operand:QI 0 "nonimmediate_operand" "q,mt"))] + "" + "@ + tst%0 + tst\t%0" + [(set_attr "length" "1,3")]) + +;;-------------------------------------------------------------------- +;;- Compare instructions +;;-------------------------------------------------------------------- + +;; - cmphi for register to memory or register compares +(define_insn "cmphi" + [(set (cc0) + (compare + (match_operand:HI 0 "general_operand" "da, mi, ??Ud") + (match_operand:HI 1 "general_operand" "mi, da, dU")))] + "" +{ + if ((REG_P (operands[0])) && (REG_P (operands[1]))) { + output_asm_insn ("pshs\t%1\t;cmphi: R:%1 with R:%0", operands); + return "cmp%0\t,s++\t;cmphi:"; + } + if (GET_CODE (operands[0]) == REG) + return "cmp%0\t%1\t;cmphi:"; + else { + cc_status.flags |= CC_REVERSED; + return "cmp%1\t%0\t;cmphi:(R)"; + } +} + [(set_attr "length" "5,5,7")]) + + +(define_insn "cmpqi" + [(set (cc0) + (compare (match_operand:QI 0 "whole_general_operand" "q,q, q,O,mt,K") + (match_operand:QI 1 "whole_general_operand" "O,mt,K,q,q, q")))] + "" +{ + if (REG_P (operands[0]) && !M_REG_P (operands[0])) + { + if (operands[1] == const0_rtx) + return "tst%0\t;cmpqi:(ZERO)"; + else + return "cmp%0\t%1\t;cmpqi:"; + } + else + { + cc_status.flags |= CC_REVERSED; + + if (operands[0] == const0_rtx) + return "tst%1\t;cmpqi:(RZERO)"; + else + return "cmp%1\t%0\t;cmpqi:(R)"; + } +} + [(set_attr "length" "1,3,2,1,3,2")]) + + +;;-------------------------------------------------------------------- +;;- Compare/branch pattern +;;-------------------------------------------------------------------- + +(define_expand "cbranchhi4" + [(set (cc0) + (compare + (match_operand:HI 1 "general_operand" "da, mi, ??Ud") + (match_operand:HI 2 "general_operand" "mi, da, dU"))) + (set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" [(cc0) (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + "" +) + +(define_expand "cbranchqi4" + [(set (cc0) + (compare + (match_operand:QI 1 "whole_general_operand" "q,q, q,O,mt,K") + (match_operand:QI 2 "whole_general_operand" "O,mt,K,q,q, q"))) + (set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" [(cc0) (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + "" +) + +;;-------------------------------------------------------------------- +;;- Move +;;-------------------------------------------------------------------- + +; this looks good (obviously not finished) but I still see 'movsi' +; places in udivsi3 where it's broken +; (define_insn "pushsi1" +; [(set (mem:SI (pre_dec (reg:HI HARD_S_REGNUM))) +; (match_operand:SI 0 "general_operand" "o")) +; (set (reg:HI HARD_S_REGNUM) +; (plus:HI (reg:HI HARD_S_REGNUM) (const_int -4))) ] +; "" +; "; pushsi %0" +; [(set_attr "length" "12")]) +; +; (define_insn "popsi1" +; [(set (match_operand:SI 0 "general_operand" "=o") +; (mem:SI (post_inc (reg:HI HARD_S_REGNUM)))) +; (set (reg:HI HARD_S_REGNUM) +; (plus:HI (reg:HI HARD_S_REGNUM) (const_int 4))) ] +; "" +; "; popsi %0" +; [(set_attr "length" "12")]) + +; (define_insn "movsi" +; [(set (match_operand:SI 0 "nonimmediate_operand" "=o") +; (match_operand:SI 1 "general_operand" " oi"))] +; "" +; "; movsi %0 <- %1" +; [(set_attr "length" "1")]) + +; this doesn't work +; (define_expand "movsi" +; [(parallel [ +; (set (match_operand:SI 0 "nonimmediate_operand" "") +; (match_operand:SI 1 "general_operand" "")) +; (clobber (match_scratch:HI 2 ""))])] +; "" +; { +; rtx insn; +; if (STACK_PUSH_P (operands[0]) || STACK_POP_P (operands[1])) +; { +; REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); +; } +; insn = emit_move_multi_word (SImode, operands[0], operands[1]); +; DONE; +; }) + + +(define_expand "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" +{ + /* One of the ops has to be in a register prior to reload */ + if (!register_operand (operand0, HImode) && + !register_operand (operand1, HImode)) + operands[1] = copy_to_mode_reg (HImode, operand1); +}) + +;;; Try a splitter to handle failure cases where we try to move +;;; an immediate constant (zero usually) directly to memory. +;;; This absolutely requires an intermediate register. +(define_split + [(set (match_operand:HI 0 "memory_operand" "") + (match_operand:HI 1 "immediate_operand" "")) + (clobber (match_operand:HI 2 "register_operand" ""))] + "" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (match_dup 2))] + "") + + +;;; This would be a nice method for loading from a word array, +;;; but it is never generated because the combiner cannot merge +;;; more than 3 instructions (there are four here). This is +;;; perhaps better done via a peephole. +(define_insn "*movhi_array_load" + [(set (match_operand:HI 0 "nonimmediate_operand" "=da") + (mem:HI (plus:HI (ashift:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%B")) (const_int 1)) + (match_operand:HI 2 "immediate_operand" "i")))) + (clobber (match_scratch:HI 3 "=X"))] + "" + "ldx\t%2\;abx\;abx\;ld%0\t,x" + [(set_attr "length" "7")]) + + +;;; Optimize the move of a byte to the stack using the pshs instruction +;;; instead of a store with pre-increment. +(define_insn "movhi_push" + [(set (match_operand:HI 0 "push_operand" "=m") + (match_operand:HI 1 "register_operand" "U"))] + "" + "pshs\t%1" + [(set_attr "length" "2")]) + + +(define_insn "*movhi_pic_symbolref" + [(set (match_operand:HI 0 "register_operand" "=a") + (match_operand:HI 1 "symbolic_operand" ""))] + "flag_pic" + "lea%0\t%c1,pcr" + [(set_attr "length" "4")]) + + +(define_insn "*movhi_1" + [(set (match_operand:HI 0 "nonimmediate_operand" "=a,d,a,ad,mu") + (match_operand:HI 1 "general_operand" " a,a,d,miu,ad"))] + "" + "@ + lea%0\t,%1 + tfr\t%1,%0 + tfr\t%1,%0 + ld%0\t%1 + st%1\t%0" + [(set_attr "length" "2,2,2,*,*")]) + + +;;; Generated by the combiner to merge an address calculation with +;;; a byte load. We can use the 'abx' instruction here. +(define_insn "*movqi_array_load" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q") + (mem:QI (plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%B")) + (match_operand:HI 2 "immediate_operand" "i")))) + (clobber (match_scratch:HI 3 "=X"))] + "" + "ldx\t%2\;abx\;ld%0\t,x" + [(set_attr "length" "6")]) + + +;;; Optimize the move of a byte to the stack using the pshs instruction +;;; instead of a store with pre-increment. +(define_insn "movqi_push" + [(set (match_operand:QI 0 "push_operand" "=m") + (match_operand:QI 1 "register_operand" " q"))] + "" + "pshs\t%1" + [(set_attr "length" "2")]) + + +;;; Optimize the move of a byte from the stack using the puls instruction +;;; instead of a store with post-decrement. +(define_insn "movqi_pop" + [(set (match_operand:QI 0 "register_operand" "=q") + (match_operand:QI 1 "pop_operand" "m"))] + "" + "puls\t%0" + [(set_attr "length" "2")]) + + +;;- load low byte of 16-bit data into 8-bit register/memory +(define_insn "*mov_lsb" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,m,!q") + (subreg:QI (match_operand:HI 1 "general_operand" "d,m,a,d, U") 1))] + "" + "@ + \t;movlsbqihi: D->B + ld%0\t%L1\t;movlsbqihi: msb:%1 -> R:%0 + tfr\t%1,d\t;movlsbqihi: R:%1 -> R:%0 + stb\t%0\t;movlsbqihi: R:%1 -> %0 + pshs\t%1\t;movlsbqihi: R:%1 -> R:%0\;leas\t1,s\;puls\t%0" + [(set_attr "length" "0,*,2,*,6")]) + + +;;- load high byte of 16-bit data into 8-bit register/memory +(define_insn "*mov_msb" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,q,m,!q") + (subreg:QI (match_operand:HI 1 "general_operand" "d,O,a,m,d, U") 0))] + "" + "@ + tfr\ta,b\t;movmsbqihi: D->B + clr%0\t\t;movmsbqihi: ZERO -> R:%0 + tfr\t%1,d\t;movmsbqihi: R:%1 -> R:%0\;tfr\ta,b + ld%0\t%L1\t;movmsbqihi: lsb:%1 -> R:%0 + sta\t%0\t;movmsbqihi: R:%1 -> %0 + pshs\t%1\t;movmsbqihi: R:%1 -> R:%0\;puls\t%0\;leas\t1,s" + [(set_attr "length" "2,1,4,*,*,6")]) + + +(define_insn "*movqi_boolean" + [(set (reg:QI HARD_Z_REGNUM) + (match_operand:QI 0 "general_operand" "q,O,i,m"))] + "" + "@ + tst%0 + andcc\t#~4 + orcc\t#4 + tst\t%0") + + +(define_insn "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,m,q,m,q,z") + (match_operand:QI 1 "general_operand" " q,O,O,mi,q,z,q"))] + "" + "@ + tfr\t%1,%0 + clr%0 + clr\t%0 + ld%0\t%1 + st%1\t%0 + tfr\tcc,%0\;and%0\t#4 + tst%0" + [(set_attr "length" "2,1,3,*,*,4,1")]) + + +;;-------------------------------------------------------------------- +;;- Swap registers +;;-------------------------------------------------------------------- + +; Note: 8-bit swap is never needed so it is not defined. + +(define_insn "swaphi" + [(set (match_operand:HI 0 "register_operand" "+r") + (match_operand:HI 1 "register_operand" "+r")) + (set (match_dup 1) (match_dup 0))] + "" + "exg\t%1,%0" + [(set_attr "length" "2") + (set (attr "cycles") (const_int EXG_CYCLES))]) + + +(define_insn "bswaphi2" + [(set (match_operand:HI 0 "register_operand" "=d") + (bswap:HI (match_operand:HI 1 "register_operand" "0")))] + "" + "exg\ta,b" + [(set_attr "length" "2")]) + + +;;-------------------------------------------------------------------- +;;- Extension and truncation insns. +;;-------------------------------------------------------------------- + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=d") + (sign_extend:HI (match_operand:QI 1 "general_operand" "B")))] + "" + "sex\t\t;extendqihi2: R:%1 -> R:%0" + [(set_attr "length" "1")]) + + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=d") + (zero_extend:HI (match_operand:QI 1 "general_operand" "B")))] + "" + "clra\t\t;zero_extendqihi: R:%1 -> R:%0" + [(set_attr "length" "1")]) + + +;;-------------------------------------------------------------------- +;;- All kinds of add instructions. +;;-------------------------------------------------------------------- + + +;; +;; gcc's automatic version of addsi3 doesn't know about adcb,adca +;; so it is MUCH less efficient. Define this one ourselves. +;; +;; TODO - can't always get 'd' for the clobber... allow other registers +;; as well and use exg d,R ... exg R,d around the code sequence to +;; use others, at a price. Also consider libcall for this when +;; optimizing for size. +;; +(define_insn "addsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=o") + (plus:SI (match_operand:SI 1 "general_operand" "%o") + (match_operand:SI 2 "general_operand" " oi"))) + (clobber (match_scratch:HI 3 "=d"))] + "" +{ + m6809_output_addsi3 (PLUS, operands); + return ""; +} + [(set_attr "length" "21")]) + + +; Increment of a 16-bit MEM by 1 can be done without a register. +(define_insn "*addhi_mem_1" + [(set (match_operand:HI 0 "memory_operand" "=m") + (plus:HI (match_dup 0) (const_int 1)))] + "GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF" +{ + rtx xoperands[2]; + + xoperands[0] = operands[0]; + xoperands[1] = adjust_address (operands[0], QImode, 1); + + output_asm_insn ("inc\t%1", xoperands); + output_asm_insn ("bne\t__IL%=", xoperands); + output_asm_insn ("inc\t%0\;__IL%=:", xoperands); + return ""; +} + [(set_attr "length" "7")]) + + +; Decrement of a 16-bit MEM by 1 can be done without a register. +(define_insn "*addhi_mem_minus1" + [(set (match_operand:HI 0 "memory_operand" "=m") + (plus:HI (match_dup 0) (const_int -1)))] + "GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF" +{ + rtx xoperands[2]; + + xoperands[0] = operands[0]; + xoperands[1] = adjust_address (operands[0], QImode, 1); + + output_asm_insn ("tst\t%1", xoperands); + output_asm_insn ("bne\t__IL%=", xoperands); + output_asm_insn ("dec\t%0", xoperands); + output_asm_insn ("__IL%=:", xoperands); + output_asm_insn ("dec\t%1", xoperands); + return ""; +} + [(set_attr "length" "7")]) + + +; Allow the addition of an 8-bit quantity to a 16-bit quantity +; using the LEAX B,Y addressing mode, where X and Y are both +; index registers. This will only get generated via the peephole +; which removes a sign extension. +(define_insn "*addhi_b" + [(set (match_operand:HI 0 "index_register_operand" "=a") + (plus:HI(match_operand:HI 1 "index_register_operand" "%a") + (match_operand:QI 2 "register_operand" "q") + ))] + "" + "lea%0\t%2,%1\t;addhi_b: R:%0 = R:%2 + R:%1" + [(set_attr "length" "*")]) + + +; Splitter for addhi pattern #5 below +(define_split + [(set (match_operand:HI 0 "index_register_operand" "") + (plus:HI (match_dup 0) (match_operand:HI 1 "memory_operand" "")))] + "reload_completed" + [ + (parallel [(set (match_dup 0) (reg:HI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (match_dup 0))]) + (set (reg:HI HARD_D_REGNUM) + (plus:HI (reg:HI HARD_D_REGNUM) (match_dup 1))) + (parallel [(set (match_dup 0) (reg:HI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (match_dup 0))]) + ] +{ +}) + + +; Splitter for addhi pattern #7 below +(define_split + [(set (match_operand:HI 0 "index_register_operand" "") + (plus:HI (match_dup 0) (match_operand:HI 1 "index_register_operand" "")))] + "reload_completed" + [ + (parallel [(set (match_dup 1) (reg:HI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (match_dup 1))]) + (set (match_dup 0) + (plus:HI (reg:HI HARD_D_REGNUM) (match_dup 0))) + (parallel [(set (match_dup 1) (reg:HI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (match_dup 1))]) + ] +{ +}) + + +; TODO - this is ugly. During RTL generation, we don't know what registers +; are available, so the multiple-insn sequences can only be solved +; via 'define_split's during matching. See andhi3 for an example. +; Keep the constraints with ? modifiers to help reload pick the right +; registers. +; +; The forms are: +; 1. D += D, expand this into a shift instead. (rtx costs should be corrected +; to avoid this even happening...) +; 2. D += U, require U to be pushed to memory. (Lots of patterns do this +; now, is this a better way?) +; 3. Best choice: 'addd' +; 4. Next best choice: 'lea' +; 5. Hybrid of 3 and 4 +; 6. Same as 4, not bad +; 7. BAD, no D register at all +; 8. 'lea', as good as 4. +(define_insn "addhi3" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d, d, d, a,?a, a,???T,a") + (plus:HI(match_operand:HI 1 "add_general_operand" "%0, 0, 0, d, 0, a, 0, a") + (match_operand:HI 2 "general_operand" " 0, !U, mi, a, m, d, T, i") + ))] + "" + "@ + lslb\t\t;addhi: R:%0 += R:%2\;rola\t\t;also R:%0 *= 2 + pshs\t%2\t;addhi: R:%0 += R:%2\;add%0\t,s++ + add%0\t%2 + lea%0\t%1,%2 + # + lea%0\t%2,%1 + # + lea%0\t%a2,%1" + [(set_attr "length" "2,6,*,*,7,*,7,*")]) + + +(define_insn "addqi3_carry" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q") + (unspec:QI [ + (match_operand:QI 1 "whole_general_operand" "%0") + (match_operand:QI 2 "whole_general_operand" "tmi")] UNSPEC_ADD_CARRY))] + "" + "adc%0\t%2\t;addqi_carry: R:%0 += %2" + [(set_attr "length" "*")]) + + +; TODO: specifying 'A' for the first constraint, to force into the A register +; is not working because of the way registers are currently set up. This will +; take some work to get right. Thus the second alternative as a backup. +(define_insn "addqi3_decimal" + [(set (match_operand:QI 0 "nonimmediate_operand" "=A,?q") + (unspec:QI [ + (match_operand:QI 1 "general_operand" "%0,0") + (match_operand:QI 2 "general_operand" "mi,mi")] UNSPEC_ADD_DECIMAL))] + "" + "@ + adda\t%2\;daa + tfr\t%0,a\;adda\t%2\;daa\;tfr\ta,%0" + [(set_attr "length" "5,9")]) + + +(define_insn "addqi3" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q,q,m,m,q") + (plus:QI (match_operand:QI 1 "whole_general_operand" "%0,0,0,0,0,0") + (match_operand:QI 2 "whole_general_operand" " 0,I,N,I,N,mi")))] + "" + "@ + asl%0\t\t;addqi: R:%0 = R:%0 + R:%0 + inc%0 + dec%0 + inc\t%0 + dec\t%0 + add%0\t%2" + [(set_attr "length" "1,1,1,3,3,*")]) + + +;;-------------------------------------------------------------------- +;;- Subtract instructions. +;;-------------------------------------------------------------------- + +(define_insn "subsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=o") + (minus:SI (match_operand:SI 1 "general_operand" " o") + (match_operand:SI 2 "general_operand" " oi"))) + (clobber (match_scratch:HI 3 "=d"))] + "" +{ + m6809_output_addsi3 (MINUS, operands); + return ""; +} + [(set_attr "length" "21")]) + + +(define_insn "subhi3" + [(set (match_operand:HI 0 "register_operand" "=d, d, a") + (minus:HI (match_operand:HI 1 "register_operand" "0, 0, 0") + (match_operand:HI 2 "general_operand" "mi, ?U,n")))] + "" + "@ + sub%0\t%2\t;subhi: R:%0 -= %2 + pshs\t%2\t;subhi: R:%0 -= R:%2\;sub%0\t,s++ + lea%0\t%n2,%1\t;subhi: R:%0 = R:%1 + %n2" + [(set_attr "length" "*,5,3")]) + + +(define_insn "subqi3_carry" + [(set (match_operand:QI 0 "register_operand" "=q") + (unspec:QI [ + (match_operand:QI 1 "whole_general_operand" "%0") + (match_operand:QI 2 "whole_general_operand" "tmi")] UNSPEC_SUB_CARRY))] + "" + "sbc%0\t%2\t;subqi_carry: R:%0 += %2" + [(set_attr "length" "*")]) + + +(define_insn "subqi3" + [(set (match_operand:QI 0 "register_operand" "=q, q, !q, q") + (minus:QI (match_operand:QI 1 "whole_register_operand" "0, 0, I, 0") + (match_operand:QI 2 "whole_general_operand" "I, mi, 0, t")))] + "" + "@ + dec%0 + sub%0\t%2 + dec%0\;neg%0 + sub%0\t%2" + [(set_attr "length" "1,3,2,3")]) + + +;;-------------------------------------------------------------------- +;;- Multiply instructions. +;;-------------------------------------------------------------------- + +; TODO - merge these two instructions, using 'extend_operator' to match +; either signed or zero extension. Everything else is the same. +(define_insn "mulqihi3" + [(set (match_operand:HI 0 "register_operand" "=d") + (mult:HI (sign_extend:HI (match_operand:QI 1 "general_operand" "%q")) + (match_operand:QI 2 "general_operand" "tmK")))] + "" + "lda\t%2\t;mulqihi3\;mul" + [(set_attr "length" "3")]) + + +(define_insn "umulqihi3" + [(set (match_operand:HI 0 "register_operand" "=d") + (mult:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "%q")) + (match_operand:QI 2 "general_operand" "tmK")))] + "" + "lda\t%2\t;umulqihi3\;mul" + [(set_attr "length" "3")]) + + +; Expand a 16x16 multiplication into either a libcall or a shift. +; If the second operand is a small constant, use the above form. +; Otherwise, do a libcall. +(define_expand "mulhi3" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (mult:HI (match_operand:HI 1 "general_operand" "") + (match_operand:HI 2 "nonmemory_operand" "")))] + "" +{ + emit_libcall_insns (HImode, "mulhi3", operands, 2); + DONE; +}) + + +;;-------------------------------------------------------------------- +;;- Divide instructions. +;;-------------------------------------------------------------------- + +(define_expand "divhi3" + [(set (match_operand:HI 0 "register_operand" "") + (div:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (HImode, "divhi3", operands, 2); + DONE; +}) + + +(define_expand "divqi3" + [(set (match_operand:QI 0 "register_operand" "") + (div:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (QImode, "divqi3", operands, 2); + DONE; +}) + + +(define_expand "udivhi3" + [(set (match_operand:HI 0 "register_operand" "") + (udiv:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (HImode, "udivhi3", operands, 2); + DONE; +}) + + +;;-------------------------------------------------------------------- +;;- mod +;;-------------------------------------------------------------------- + +(define_expand "modhi3" + [(set (match_operand:HI 0 "register_operand" "") + (mod:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (HImode, "modhi3", operands, 2); + DONE; +}) + + +(define_expand "modqi3" + [(set (match_operand:QI 0 "register_operand" "") + (mod:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (QImode, "modqi3", operands, 2); + DONE; +}) + + +(define_expand "umodhi3" + [(set (match_operand:HI 0 "register_operand" "") + (umod:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_operand" "")))] + "" +{ + emit_libcall_insns (HImode, "umodhi3", operands, 2); + DONE; +}) + + + +;;-------------------------------------------------------------------- +;;- and, or, xor common patterns +;;-------------------------------------------------------------------- + +; Split a bitwise HImode into two QImode instructions, with one of +; the sources in a pushable register. The register is pushed onto +; the stack and memory pop operands (,s+) are used in the QI forms. +(define_split + [(set (match_operand:HI 0 "register_operand" "") + (match_operator:HI 3 "logical_bit_operator" + [(match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_operand" "")]))] + "reload_completed" + [(set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 2)) + (set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_A_REGNUM) + (mem:QI (post_inc:QI (reg:HI HARD_S_REGNUM)))])) + (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_D_REGNUM) + (mem:QI (post_inc:QI (reg:HI HARD_S_REGNUM)))])) + (use (reg:QI HARD_A_REGNUM))] +{ +}) + +; Split a bitwise HImode into two QImode instructions, with one +; of the sources being a (MEM (MEM (...)); i.e. an indirect memory +; reference. This requires dereferencing the pointer into a +; temporary register (X), which must be saved/restored around the +; compute instructions. +(define_split + [(set (match_operand:HI 0 "register_operand" "") + (match_operator:HI 3 "logical_bit_operator" + [(match_operand:HI 1 "register_operand" "") + (mem:HI (match_operand:HI 2 "memory_operand" ""))]))] + "reload_completed" + [ + (set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 4)) + (set (match_dup 4) (match_dup 2)) + (set (match_dup 4) (mem:HI (match_dup 4))) + (set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_A_REGNUM) + (mem:QI (post_inc:QI (match_dup 4)))])) + (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_D_REGNUM) + (mem:QI (post_inc:QI (match_dup 4)))])) + (use (reg:QI HARD_A_REGNUM)) + (set (match_dup 4) (mem:HI (post_inc:HI (reg:HI HARD_S_REGNUM)))) + ] +{ + /* Use X for a temporary index register */ + operands[4] = gen_rtx_REG (HImode, HARD_X_REGNUM); +}) + + +; Split a bitwise HImode into two QImode instructions. This is +; the common case. This handles splitting when neither of the +; above two cases applies. +(define_split + [(set (match_operand:HI 0 "register_operand" "") + (match_operator:HI 3 "logical_bit_operator" + [(match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "general_operand" "")]))] + "reload_completed" + [(set (reg:QI HARD_A_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_A_REGNUM) (match_dup 4)])) + (set (reg:QI HARD_D_REGNUM) (match_op_dup:QI 3 + [(reg:QI HARD_D_REGNUM) (match_dup 5)])) + (use (reg:QI HARD_A_REGNUM))] +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[4] = gen_rtx_const_high (operands[2]); + operands[5] = gen_rtx_const_low (operands[2]); + } + else if ((GET_CODE (operands[2]) == MEM) + && (GET_CODE (XEXP (operands[2], 0)) == MEM)) + { + FAIL; + } + else + { + operands[4] = gen_highpart (QImode, operands[2]); + operands[5] = gen_lowpart (QImode, operands[2]); + } +}) + +; Below are the specific cases for each of the operators. +; The QImode versions are the simplest and can be implemented +; directly on the hardware. The HImode cases are all output +; using one of the above splitting techniques. + +;;-------------------------------------------------------------------- +;;- and +;;-------------------------------------------------------------------- + +(define_insn "andhi3" + [(set (match_operand:HI 0 "register_operand" "=d") + (and:HI (match_operand:HI 1 "register_operand" "%0") + (match_operand:HI 2 "general_operand" "mnU")))] + "" + "#") + +;; it is not clear that this is correct +(define_insn "*andqi_2" + [(set + (match_operand:QI 0 "register_operand" "=q") + (and:QI (match_operand:QI 1 "register_operand" "q") + (match_operand 2 "const_int_operand" "i")))] + "" +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[3] = GEN_INT(INTVAL(operands[2]) & 0xff); + return "and%0 %3"; + } + + return "and%0 %2"; +} + [(set_attr "length" "2")]) + +(define_insn "andqi3" + [(set (match_operand:QI 0 "register_operand" "=q,q,q,qc") + (and:QI (match_operand:QI 1 "whole_register_operand" "%0,0,0,0") + (match_operand:QI 2 "whole_general_operand" " O,N,m,i")))] + "" + "@ + clr%0\t;andqi(ZERO) + \t;andqi(-1) + and%0\t%2 + and%0\t%2" + [(set_attr "length" "1,0,3,2")]) + + +;;-------------------------------------------------------------------- +;;- or +;;-------------------------------------------------------------------- + +(define_insn "iorhi3" + [(set (match_operand:HI 0 "register_operand" "=d") + (ior:HI (match_operand:HI 1 "register_operand" "%0") + (match_operand:HI 2 "general_operand" "mnU")))] + "" + "#") + + +(define_insn "iorqi3" + [(set (match_operand:QI 0 "register_operand" "=q,q, qc") + (ior:QI (match_operand:QI 1 "whole_register_operand" "%0,0, 0") + (match_operand:QI 2 "whole_general_operand" " O,m,i")))] + "" + "@ + \t;iorqi(ZERO) + or%0\t%2 + or%0\t%2" + [(set_attr "length" "0,3,2")]) + +;;-------------------------------------------------------------------- +;;- xor +;;-------------------------------------------------------------------- + +(define_insn "xorhi3" + [(set (match_operand:HI 0 "register_operand" "=d") + (xor:HI (match_operand:HI 1 "register_operand" "%0") + (match_operand:HI 2 "general_operand" "mnU")))] + "" + "#") + + +(define_insn "xorqi3" + [(set (match_operand:QI 0 "register_operand" "=q,q,q,q") + (xor:QI (match_operand:QI 1 "whole_register_operand" "%0,0,0,0") + (match_operand:QI 2 "whole_general_operand" " O,N,m,i")))] + "" + "@ + \t;xorqi(ZERO) + com%0\t;xorqi(-1) + eor%0\t%2 + eor%0\t%2" + [(set_attr "length" "0,1,3,2")]) + +;;-------------------------------------------------------------------- +;;- Two's Complements +;;-------------------------------------------------------------------- + +(define_insn "neghi2" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,!a") + (neg:HI (match_operand:HI 1 "general_operand" "0, 0")))] + "" + "@ + nega\;negb\;sbca\t#0 + exg\td,%0\;nega\;negb\;sbca\t#0\;exg\td,%0" + [(set_attr "length" "5,9")]) + + +(define_insn "negqi2" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m") + (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))] + "" + "@ + neg%0 + neg\t%0" + [(set_attr "length" "1,3")]) + + +;;-------------------------------------------------------------------- +;;- One's Complements +;;-------------------------------------------------------------------- + +(define_insn "one_cmplhi2" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,?tm,???a") + (not:HI (match_operand:HI 1 "general_operand" "0, 0, 0")))] + "" + "@ + coma\;comb + com\t%0\;com\t%L0 + exg\td,%0\;coma\;comb\;exg\td,%0" + [(set_attr "length" "2,6,6")]) + + +(define_insn "one_cmplqi2" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q,m") + (not:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")))] + "" + "@ + com%0 + com\t%0" + [(set_attr "length" "1,3")]) + +;;-------------------------------------------------------------------- +;;- Shifts/rotates +;;-------------------------------------------------------------------- + +(define_code_iterator bit_code [ashift ashiftrt lshiftrt]) +(define_code_attr bit_code_name [(ashift "ashl") (ashiftrt "ashr") (lshiftrt "lshr")]) + +(define_mode_iterator bit_mode [QI HI]) +(define_mode_attr bit_mode_name [(QI "qi3") (HI "hi3")]) + +;; Emit RTL for any shift (handles all 3 opcodes and 2 mode sizes) + +(define_expand "<bit_code:bit_code_name><bit_mode:bit_mode_name>" + [(set (match_operand:bit_mode 0 "nonimmediate_operand" "") + (bit_code:bit_mode (match_operand:bit_mode 1 "general_operand" "") + (match_operand:bit_mode 2 "nonmemory_operand" "")))] + "" +{ +}) + +; Individual instructions implemented in the CPU. + + +(define_insn "*ashift1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q") + (ashift:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))] + "" + "@ + asl\t%0 + asl%0" + [(set_attr "length" "3,1")]) + +(define_insn "*lshiftrt1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q") + (lshiftrt:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))] + "" + "@ + lsr\t%0 + lsr%0" + [(set_attr "length" "3,1")]) + +(define_insn "*ashiftrt1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q") + (ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))] + "" + "@ + asr\t%0 + asr%0" + [(set_attr "length" "3,1")]) + +(define_insn "*rotate1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q") + (rotate:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))] + "" + "@ + rol\t%0 + rol%0" + [(set_attr "length" "3,1")]) + + +(define_insn "*rotatert1" + [(set (match_operand:QI 0 "nonimmediate_operand" "=m,q") + (rotatert:QI (match_operand:QI 1 "general_operand" "0,0") (const_int 1)))] + "" + "@ + ror\t%0 + ror%0" + [(set_attr "length" "3,1")]) + + +; A shift by 8 for D reg can be optimized by just moving +; between the A/B halves, and then zero/sign extending or +; filling in zeroes. +; Because GCC does not understand that 'A' and 'D' refer to +; the same storage location, we must use 'USE' throughout +; to prevent deletion of 'unnecessary' instructions. +; Similar optimization for MEM would require a scratch register +; so is not done here. + +(define_split + [(set (reg:HI HARD_D_REGNUM) (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 8)))] + "reload_completed" + [ + (use (reg:HI HARD_D_REGNUM)) + (set (reg:QI HARD_A_REGNUM) (reg:QI HARD_D_REGNUM)) + (use (reg:QI HARD_A_REGNUM)) + (set (reg:QI HARD_D_REGNUM) (const_int 0)) + ] + "") + +(define_split + [(set (reg:HI HARD_D_REGNUM) (lshiftrt:HI (reg:HI HARD_D_REGNUM) (const_int 8)))] + "reload_completed" + [ + (use (reg:HI HARD_D_REGNUM)) + (set (reg:QI HARD_D_REGNUM) (reg:QI HARD_A_REGNUM)) + (use (reg:QI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (zero_extend:HI (reg:QI HARD_D_REGNUM))) + ] + "") + +(define_split + [(set (reg:HI HARD_D_REGNUM) (ashiftrt:HI (reg:HI HARD_D_REGNUM) (const_int 8)))] + "reload_completed" + [ + (use (reg:HI HARD_D_REGNUM)) + (set (reg:QI HARD_D_REGNUM) (reg:QI HARD_A_REGNUM)) + (use (reg:QI HARD_D_REGNUM)) + (set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM))) + ] + "") + + +; On the WPC hardware, there is a shift register that can be used +; to compute (1<<n) efficiently in two instructions. Note that this +; form only works when using -mint8 though, because C will promote +; to 'int' when doing this operation. TODO : we need a 16-bit form too. +(define_insn "ashlqi3_wpc" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q") + (ashift:QI (match_operand:QI 1 "immediate_operand" "I") + (match_operand:QI 2 "general_operand" "q")))] + "TARGET_WPC" + "st%2\t0x3FF7\;ld%0\t0x3FF7" + [(set_attr "length" "6")]) + + +; Internal instructions for shifting by a constant. +; Two forms are provided, one for QImode, one for HImode. +; These are always split into the above instructions +; (except for QImode forms that directly match one of the +; above instructions, in which the condition will not +; allow the splitter to match). + +(define_insn_and_split "<bit_code:bit_code_name>hi3_const" + [(set (match_operand:HI 0 "nonimmediate_operand" "=dm") + (bit_code:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "immediate_operand" "n")))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + m6809_split_shift (<bit_code:CODE>, operands); + DONE; +}) + + +(define_insn_and_split "<bit_code:bit_code_name>qi3_const" + [(set (match_operand:QI 0 "nonimmediate_operand" "=qm") + (bit_code:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "immediate_operand" "n")))] + "INTVAL (operands[2]) > 1" + "#" + "&& reload_completed" + [(const_int 0)] +{ + m6809_split_shift (<bit_code:CODE>, operands); + DONE; +}) + +; Internal instructions for shifting by a nonconstant. +; These expand into complex assembly. + +(define_insn "<bit_code:bit_code_name>hi3_reg" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d") + (bit_code:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "nonimmediate_operand" "v")))] + "" +{ + m6809_output_shift_insn (<bit_code:CODE>, operands); + return ""; +} + [(set_attr "length" "20")]) + + +(define_insn "<bit_code:bit_code_name>qi3_reg" + [(set (match_operand:QI 0 "nonimmediate_operand" "=q") + (bit_code:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "nonimmediate_operand" "v")))] + "" +{ + m6809_output_shift_insn (<bit_code:CODE>, operands); + return ""; +} + [(set_attr "length" "16")]) + + + +;;-------------------------------------------------------------------- +;;- Jumps and transfers +;;-------------------------------------------------------------------- + +;;; The casesi pattern is normally *not* defined; see 'tablejump' instead. +(define_expand "casesi" + [(match_operand:HI 0 "register_operand" "") ; index to jump on + (match_operand:HI 1 "immediate_operand" "") ; lower bound + (match_operand:HI 2 "immediate_operand" "") ; total range + (match_operand 3 "" "") ; table label + (match_operand 4 "" "")] ; out of range label + "TARGET_BYTE_INT && TARGET_CASESI" +{ + m6809_do_casesi (operands[0], operands[1], operands[2], + operands[3], operands[4]); + DONE; +}) + +(define_insn "tablejump_short_offset" + [(set (pc) + (mem:HI (plus:HI (match_operand:HI 1 "register_operand" "U") + (zero_extend:HI (match_operand:QI 0 "register_operand" "q")))))] + "" + "jmp\t[b,x]\t;tablejump_short_offset" + [(set_attr "length" "3")]) + +(define_insn "tablejump_long_offset" + [(set (pc) + (mem:HI (plus:HI (match_operand:HI 1 "register_operand" "U") + (match_operand:HI 0 "register_operand" "d"))))] + "" + "jmp\t[d,x]\t;tablejump_long_offset" + [(set_attr "length" "3")]) + + + ;; A tablejump operation gives the address in operand 0, with the + ;; CODE_LABEL for the table in operand 1. The 'define_expand' + ;; shows the arguments as GCC presents them. For a register + ;; operand, the assembly code is straightforward. For a MEM, + ;; assumed to be a SYMBOL_REF, two forms are given, one normal + ;; and one for PIC mode. + (define_expand "tablejump" + [(parallel [ + (set (pc) (match_operand:HI 0 "" "")) + (use (label_ref (match_operand 1 "" ""))) + (clobber (match_scratch:HI 2 "")) + ])] + "" + { + }) + + +(define_insn "*tablejump_reg" + [(parallel [ + (set (pc) + (match_operand:HI 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" ""))) + (clobber (match_scratch:HI 2 "")) + ])] + "" + "jmp\t,%0" + [(set_attr "length" "3")]) + + +(define_insn "*tablejump_symbol" + [(parallel [ + (set (pc) + (mem:HI + (plus:HI (match_operand:HI 0 "register_operand" "a") + (label_ref (match_operand 1 "" ""))))) + (use (label_ref (match_dup 1))) + (clobber (match_scratch:HI 2 "")) + ])] + "!flag_pic" +{ + output_asm_insn ("jmp\t[%a1,%0]", operands); + return ""; +} + [(set_attr "length" "4")]) + + +(define_insn "*tablejump_symbol_pic" + [(parallel [ + (set (pc) + (mem:HI + (plus:HI (match_operand:HI 0 "register_operand" "d") + (label_ref (match_operand 1 "" ""))))) + (use (label_ref (match_dup 1))) + (clobber (match_scratch:HI 2 "=&a")) + ])] + "flag_pic" +{ + output_asm_insn ("lea%2\t%a1,pcr", operands); + output_asm_insn ("ld%0\t%0,%2", operands); + output_asm_insn ("jmp\t%0,%2", operands); + return ""; +} + [(set_attr "length" "8")]) + + +(define_insn "indirect_jump" + [(set (pc) + (match_operand:HI 0 "register_operand" "a"))] + "" + "jmp\t,%0" + [(set_attr "length" "3")]) + + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" +{ + return output_branch_insn ( LABEL_REF, operands, get_attr_length (insn)); +} + [(set (attr "type") (const_string "branch"))]) + +; Output assembly for a condition branch instruction. +(define_insn "*cond_branch" + [(set (pc) + (if_then_else + (match_operator 1 "comparison_operator" [(cc0) (const_int 0)]) + (label_ref (match_operand 0 "" "")) (pc)))] + "" +{ + return output_branch_insn ( GET_CODE(operands[1]), + operands, get_attr_length (insn)); +} + [(set (attr "type") (const_string "cbranch"))]) + + +; Similar to above, but for a condition branch instruction that +; had its operands reversed at some point. +(define_insn "*cond_branch_reverse" + [(set (pc) + (if_then_else + (match_operator 1 "comparison_operator" [(cc0) (const_int 0)]) + (pc) (label_ref (match_operand 0 "" ""))))] + "" +{ + return output_branch_insn ( reverse_condition (GET_CODE(operands[1])), + operands, get_attr_length (insn)); +} + [(set (attr "type") (const_string "cbranch"))]) + + + +;;-------------------------------------------------------------------- +;;- Calls +;;-------------------------------------------------------------------- + +;; Generate a call instruction for a function that does not +;; return a value. The expander is used during RTL generation. +;; The instructions below are used during matching; only one +;; of them will be used, depending on the type of function +;; being called. The different conditions are: +;; +;; 1) far_functionp - is this a far function? Those need +;; to be output as indirect calls through a far-function +;; handler. +;; +;; 2) noreturn_functionp - if the function does not return, +;; we can use a 'jmp' instead of a 'jsr' to call it. +;; +;; 3) is PIC mode enabled? If so, we'll always use +;; relative calls (lbsr or lbra). +;; +;; Note: not all combinations are fully supported, especially +;; relating to PIC. +;; +;; The 'bsr' instruction is never generated. + +(define_expand "call" + [(call (match_operand:HI 0 "memory_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + "") + +(define_insn "*call_nopic_far" + [(call (match_operand:HI 0 "memory_operand" "m") + (match_operand:HI 1 "general_operand" "g"))] + "far_functionp (operands[0])" +{ + output_far_call_insn (operands, 0); + return ""; +} + [(set_attr "length" "6")]) + + +; PIC forms come first, and should only match +; (MEM (SYMBOL_REF)). Other MEM forms are treated as usual. +(define_insn "*call_pic" + [(call (mem:HI (match_operand:HI 0 "symbolic_operand" "")) + (match_operand:HI 1 "general_operand" "g"))] + "flag_pic && !noreturn_functionp (operands[0])" + "lbsr\t%C0" + [(set_attr "length" "4")]) + + +(define_insn "*call_nopic" + [(call (match_operand:HI 0 "memory_operand" "m") + (match_operand:HI 1 "general_operand" "g"))] + "!noreturn_functionp (operands[0])" + "jsr\t%0" + [(set_attr "length" "3") + (set (attr "cycles") (const_int JSR_EXTENDED_CYCLES))]) + + +(define_insn "*call_noreturn_pic" + [(call (mem:HI (match_operand:HI 0 "symbolic_operand" "")) + (match_operand:HI 1 "general_operand" "g"))] + "flag_pic && noreturn_functionp (operands[0])" + "lbra\t%C0" + [(set_attr "length" "4")]) + + +(define_insn "*call_noreturn_nopic" + [(call (match_operand:HI 0 "memory_operand" "m") + (match_operand:HI 1 "general_operand" "g"))] + "noreturn_functionp (operands[0])" + "jmp\t%0" + [(set_attr "length" "3")]) + + +;; +;; Same as above, but for functions that do return a value. +;; +(define_expand "call_value" + [(set (match_operand 0 "" "") + (call (match_operand:HI 1 "memory_operand" "") + (match_operand:HI 2 "general_operand" "")))] + "" + "") + + +(define_insn "*call_value_far" + [(set (match_operand 0 "" "=gz") + (call (match_operand:HI 1 "memory_operand" "m") + (match_operand:HI 2 "general_operand" "g")))] + "far_functionp (operands[1])" +{ + output_far_call_insn (operands, 1); + return ""; +} + [(set_attr "length" "6")]) + + +(define_insn "*call_value_pic" + [(set (match_operand 0 "" "=gz") + (call (mem:HI (match_operand:HI 1 "symbolic_operand" "")) + (match_operand:HI 2 "general_operand" "g")))] + "flag_pic" + "lbsr\t%C1" + [(set_attr "length" "4")]) + + +(define_insn "*call_value_nopic" + [(set (match_operand 0 "" "=gz") + (call (match_operand:HI 1 "memory_operand" "m") + (match_operand:HI 2 "general_operand" "g")))] + "" + "jsr\t%1" + [(set_attr "length" "3") + (set (attr "cycles") (const_int JSR_EXTENDED_CYCLES))]) + + + +;; +;; How to generate an untyped call. +;; +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" +{ + int i; + + emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx)); + for (i=0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + emit_insn (gen_blockage ()); + DONE; +}) + + +(define_expand "sibcall" + [(parallel + [(call (match_operand:HI 0 "memory_operand" "") + (match_operand:HI 1 "immediate_operand" "")) + (use (reg:HI HARD_PC_REGNUM))])] + "" + "") + +(define_insn "*sibcall_1" + [(parallel + [(call (match_operand:HI 0 "memory_operand" "m") + (match_operand:HI 1 "immediate_operand" "i")) + (use (reg:HI HARD_PC_REGNUM))])] + "SIBLING_CALL_P(insn)" + "jmp\t%0" + [(set_attr "length" "4")]) + + +(define_expand "sibcall_value" + [(parallel + [(set (match_operand 0 "" "") + (call (match_operand:HI 1 "memory_operand" "") + (match_operand:HI 2 "immediate_operand" ""))) + (use (reg:HI HARD_PC_REGNUM))])] + "" + "") + +(define_insn "*sibcall_value_1" + [(parallel + [(set (match_operand 0 "" "=gz") + (call (match_operand:HI 1 "memory_operand" "m") + (match_operand:HI 2 "immediate_operand" "i"))) + (use (reg:HI HARD_PC_REGNUM))])] + "SIBLING_CALL_P(insn)" + "jmp\t%1" + [(set_attr "length" "4")]) + + +;;-------------------------------------------------------------------- +;;- Function Entry and Exit +;;-------------------------------------------------------------------- + +;; On entry to a function, the stack frame looks as follows: +;; - return address (pushed by the caller) +;; - saved registers +;; - local variable storage +;; +;; If the function does not modify the stack after that, then +;; any of these can be accessed directly as an offset from +;; STACK_POINTER_REGNUM. Otherwise, a frame pointer is required. +;; In that case, the prologue must also initialize HARD_FRAME_POINTER_REGNUM +;; and all references to the stack frame will use that as a base instead. +;; +(define_expand "prologue" + [(const_int 0)] + "prologue_epilogue_required ()" +{ + emit_prologue_insns (); + DONE; +}) + + +;; The function epilogue does exactly the reverse of the prologue, +;; deallocating local variable space, restoring saved registers, +;; and returning. +;; +;; For the 6809, the return may be 'rti' if the function was +;; declared as an interrupt function, but is normally 'rts'. +;; +;; Also, as an optimization, the register restore and the 'rts' +;; can be combined into a single instruction, by adding 'PC' to the +;; list of registers to be restored. This is only done if there are +;; any saved registers, as 'rts' is more efficient by itself. +;; +(define_expand "epilogue" + [(const_int 0)] + "prologue_epilogue_required ()" +{ + emit_epilogue_insns (false); + DONE; +}) + + +(define_expand "sibcall_epilogue" + [(const_int 0)] + "prologue_epilogue_required ()" +{ + emit_epilogue_insns (true); + DONE; +}) + + +;; The RTS instruction +(define_insn "return_rts" + [(return) + (use (reg:HI HARD_PC_REGNUM))] + "!m6809_current_function_has_type_attr_p (\"interrupt\") + && m6809_get_live_regs () == 0" + "rts" + [(set_attr "length" "1") + (set (attr "cycles") (const_int RTS_CYCLES))]) + +(define_insn "return_puls_pc" + [(return) + (use (reg:HI HARD_PC_REGNUM))] + "!m6809_current_function_has_type_attr_p (\"interrupt\") + && m6809_get_live_regs () != 0" + "" + [(set_attr "length" "1") + (set (attr "cycles") (const_int RTS_CYCLES))]) + +;; The RTI instruction +(define_insn "return_rti" + [(return) + (use (reg:HI HARD_PC_REGNUM))] + "m6809_current_function_has_type_attr_p (\"interrupt\")" + "rti" + [(set_attr "length" "1") + (set (attr "cycles") (const_int RTI_CYCLES))]) + + +;;-------------------------------------------------------------------- +;;- Unspecified instructions +;;-------------------------------------------------------------------- + +;; An instruction that has the effect of an unspec_volatile, but +;; which doesn't require emitting any assembly code. +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)] + "" + "" + [(set_attr "length" "0") + (set (attr "cycles") (const_int 0))]) + + +;; Say how to push multiple registers onto the stack, using +;; the 6809 'pshs' instruction. The operand is a regset +;; specifying which registers to push. +;; +;; The operand mode is not given intentionally, so as to allow +;; any possible integer mode for the regset. +;; +;; See below for a peephole that can combine consecutive push +;; instructions that qualify for merging. +(define_insn "register_push" + [(use (reg:HI HARD_S_REGNUM)) + (unspec_volatile + [(match_operand 0 "immediate_operand" "")] UNSPEC_PUSH_RS) + (clobber (reg:HI HARD_S_REGNUM))] + "" + "pshs\t%R0" + [(set_attr "length" "2") + (set (attr "cycles") (const_int PSH_PUL_CYCLES))]) + + +;; Say how to pop multiple registers from the stack, using +;; the 6809 'puls' instruction. The operand is the register +;; bitset value. +(define_insn "register_pop" + [(use (reg:HI HARD_S_REGNUM)) + (unspec_volatile + [(match_operand 0 "immediate_operand" "")] UNSPEC_POP_RS) + (clobber (reg:HI HARD_S_REGNUM))] + "" + "puls\t%R0" + [(set_attr "length" "2") + (set (attr "cycles") (const_int PSH_PUL_CYCLES))]) + + +(define_insn "m6809_swi" + [(unspec_volatile + [(match_operand:QI 0 "immediate_operand" "I,n")] UNSPEC_SWI)] + "" + "@ + swi + swi%c0" + [(set_attr "length" "1,2") + (set (attr "cycles") (const_int SWI_CYCLES))]) + + +;; Generate the CWAI instruction +(define_insn "m6809_cwai" + [(unspec_volatile + [(match_operand:QI 0 "immediate_operand" "")] UNSPEC_CWAI)] + "" + "cwai\t%0" + [(set_attr "length" "2") + (set (attr "cycles") (const_int CWAI_CYCLES))]) + + +;; Generate the SYNC instruction +(define_insn "m6809_sync" + [(unspec_volatile [(const_int 0)] UNSPEC_SYNC)] + "" + "sync" + [(set_attr "length" "1") + (set (attr "cycles") (const_int SYNC_CYCLES))]) + + +;; Generate the NOP instruction +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "length" "1") + (set (attr "cycles") (const_int NOP_CYCLES))]) + + +;;-------------------------------------------------------------------- +;;- Peepholes +;;-------------------------------------------------------------------- + +;;; Each peephole has an ID that is used for debugging. +;;; Each peephole condition is bracketed by calls to +;;; m6809_match_peephole2() also for debugging. +(define_constants [ + (PEEP_END 0) + (PEEP_COND 1) + + (PEEP_STACK_STORE_INC 0) + (PEEP_STACK_CLEAR_INC 1) + (PEEP_LSRB_ADCB 2) + (PEEP_ABX 3) + (PEEP_ABX2 4) + (PEEP_INDEXED_INC 5) + (PEEP_MEM_DEC 6) + (PEEP_MEM_INC 7) + (PEEP_MEM_DEC_CMP 8) + (PEEP_PUSH2 9) + (PEEP_STORE_IMPLIES_CC 10) + (PEEP_DEC_IMPLIES_CC 11) + (PEEP_LEAB 12) + (PEEP_LDX_INDIRECT 13) + (PEEP_POP_JUNK 14) +]) + + +;;; Optimize 'leas -1,s' followed by 'stb ,s'. This can happen if the +;;; function prologue needs to allocate stack space and 'b' is placed +;;; into that local right away. Combine the stack allocation with the +;;; store using preincrement mode. +(define_peephole2 + [(set (reg:HI HARD_S_REGNUM) + (plus:HI (reg:HI HARD_S_REGNUM) (const_int -1))) + (set (mem:QI (reg:HI HARD_S_REGNUM)) + (match_operand:QI 0 "register_operand" ""))] + "m6809_match_peephole2 (PEEP_STACK_STORE_INC, PEEP_END)" + [(set (mem:QI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (match_dup 0))] + "") + + +;;; Same as above, but for a 'clr ,s' that follows the prologue. +(define_peephole2 + [(set (reg:HI HARD_S_REGNUM) (plus:HI (reg:HI HARD_S_REGNUM) (const_int -1))) + (set (mem:QI (reg:HI HARD_S_REGNUM)) (const_int 0))] + "m6809_match_peephole2 (PEEP_STACK_CLEAR_INC, PEEP_END)" + [(set (mem:QI (pre_dec:HI (reg:HI HARD_S_REGNUM))) (const_int 0))] + "") + + +;;; Merge two consecutive push instructions into a single register_push. +(define_peephole2 + [(set (match_operand 0 "push_operand" "") + (match_operand 1 "register_operand" "")) + (set (match_operand 2 "push_operand" "") + (match_operand 3 "register_operand" ""))] + "m6809_match_peephole2 (PEEP_PUSH2, PEEP_COND) + && reload_completed + && GET_MODE (operands[1]) == GET_MODE (operands[3]) + && m6809_can_merge_pushpop_p (UNSPEC_PUSH_RS, 1 << REGNO (operands[1]), 1 << REGNO (operands[3])) + && m6809_match_peephole2 (PEEP_PUSH2, PEEP_END)" + [(parallel [ + (use (reg:HI HARD_S_REGNUM)) + (unspec_volatile [(match_dup 4)] UNSPEC_PUSH_RS) + (clobber (reg:HI HARD_S_REGNUM))]) + (use (match_dup 1)) + (use (match_dup 3))] +{ + operands[4] = gen_rtx_CONST_INT (QImode, + (1 << REGNO (operands[1])) | (1 << REGNO (operands[3]))); +}) + + +;;; Convert 'stX ,--s' into a push instruction. Use the regset +;;; notation, so that it may be combined with an adjacent regset. +;;; TBD - this doesn't compile some code cleanly. +;(define_peephole2 +; [(set (mem:HI (pre_dec:HI (reg:HI HARD_S_REGNUM))) +; (reg:HI HARD_X_REGNUM))] +; "reload_completed" +; [(parallel [ +; (use (reg:HI HARD_S_REGNUM)) +; (unspec_volatile [(match_dup 0)] UNSPEC_PUSH_RS) +; (clobber (reg:HI HARD_S_REGNUM))])] +;{ +; operands[0] = gen_rtx_CONST_INT (HImode, X_REGBIT); +;}) + + +;;; +;;; q = (q+1)/2 can be optimized as "lsrb; adcb". This also +;;; won't overflow when q=0xFF. +;;; TODO : this form isn't accounting for promotion when +;;; using 16-bit ints. +;;; +(define_peephole + [(set (reg:QI HARD_D_REGNUM) + (lshiftrt:QI (plus:HI (match_dup 0) (const_int 1)) (const_int 1)))] + "m6809_match_peephole2 (PEEP_LSRB_ADCB, PEEP_END)" + "lsrb\;adcb\t#0; peephole" + [(set_attr "length" "2")]) + + +;; +;; Optimize the case of following a register store with a test +;; of reg or mem just moved. +;; +(define_peephole + [(set (match_operand:HI 0 "memory_operand" "=m") + (match_operand:HI 1 "register_operand" "r")) + (set (cc0) (match_operand:HI 2 "general_operand" "g"))] + "m6809_match_peephole2 (PEEP_STORE_IMPLIES_CC, PEEP_COND) + && (operands[2] == operands[0] || operands[2] == operands[1]) + && m6809_match_peephole2 (PEEP_STORE_IMPLIES_CC, PEEP_END)" + "st%1\t%0\t;movhi: R:%1 -> %0 w/ implied test of %2" + [(set_attr "length" "4")]) + + +;; Optimize a pair of SET instructions in which the second insn +;; is the reverse of the first one. I.e. +;; +;; A = B +;; ----> A = B +;; B = A +;; +;; The second insn is redundant. Define two patterns, one for QI, one for HI. +;; But don't do this if either is a VOLATILE MEM. +(define_peephole2 + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "nonimmediate_operand" "")) + (set (match_dup 1) (match_dup 0))] + "!MEM_P (operands[0]) || !MEM_P (operands[1]) || (!MEM_VOLATILE_P (operands[0]) && !MEM_VOLATILE_P (operands[1]))" + [(set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_dup 1) (match_dup 0))] + "!MEM_P (operands[0]) || !MEM_P (operands[1]) || (!MEM_VOLATILE_P (operands[0]) && !MEM_VOLATILE_P (operands[1]))" + [(set (match_dup 0) (match_dup 1))] + "") + + +;; +;; Optimize the sum of an 8-bit and 16-bit using the 'abx' instruction +;; if B and X can be used. Two patterns are provided to catch both +;; X=X+D and X=D+X. +;; +(define_peephole + [(set (reg:HI HARD_D_REGNUM) + (zero_extend:HI (match_operand:QI 0 "general_operand" "q"))) + (set (reg:HI HARD_X_REGNUM) + (plus:HI (reg:HI HARD_D_REGNUM) (reg:HI HARD_X_REGNUM)))] + "m6809_match_peephole2 (PEEP_ABX, PEEP_END)" + "abx" + [(set_attr "length" "1")]) + +(define_peephole + [(set (reg:HI HARD_D_REGNUM) + (zero_extend:HI (match_operand:QI 0 "general_operand" "q"))) + (set (reg:HI HARD_X_REGNUM) + (plus:HI (reg:HI HARD_X_REGNUM) (reg:HI HARD_D_REGNUM)))] + "m6809_match_peephole2 (PEEP_ABX, PEEP_END)" + "abx" + [(set_attr "length" "1")]) + +;;; Likewise, handle when B is scaled by 2 prior to the add. +;;; Instead of shifting B in 4 cycles, just do the ABX a second +;;; time, in only 3 cycles. + +(define_peephole + [(set (reg:HI HARD_D_REGNUM) + (zero_extend:HI (match_operand:QI 0 "general_operand" "q"))) + (set (reg:HI HARD_D_REGNUM) + (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 1))) + (set (reg:HI HARD_X_REGNUM) + (plus:HI (reg:HI HARD_D_REGNUM) (reg:HI HARD_X_REGNUM)))] + "m6809_match_peephole2 (PEEP_ABX2, PEEP_END)" + "abx\;abx" + [(set_attr "length" "2")]) + +(define_peephole + [(set (reg:HI HARD_D_REGNUM) + (zero_extend:HI (match_operand:QI 0 "general_operand" "q"))) + (set (reg:HI HARD_D_REGNUM) + (ashift:HI (reg:HI HARD_D_REGNUM) (const_int 1))) + (set (reg:HI HARD_X_REGNUM) + (plus:HI (reg:HI HARD_X_REGNUM) (reg:HI HARD_D_REGNUM)))] + "m6809_match_peephole2 (PEEP_ABX2, PEEP_END)" + "abx\;abx" + [(set_attr "length" "2")]) + + +;; +;; Work around a compiler bug that generates bad code when copying +;; between 32-bit memory addresses after a libcall. The problem seen is +;; that the source is MEM (REG X), but X is used as the reload register. +;; The second half of the copy therefore fails. +;; +;; The solution is to switch the reload register to D, since that is guaranteed +;; not to be in use right after a libcall. +;; +(define_peephole2 + [(set (reg:HI HARD_X_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM))) + (set (match_operand:HI 0 "nonimmediate_operand" "") (reg:HI HARD_X_REGNUM)) + (set (reg:HI HARD_X_REGNUM) + (mem:HI (plus:HI (reg:HI HARD_X_REGNUM) (const_int 2)))) + (set (match_operand:HI 1 "nonimmediate_operand" "") (reg:HI HARD_X_REGNUM))] + "reload_completed" + [(set (reg:HI HARD_D_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM))) + (set (match_dup 0) (reg:HI HARD_D_REGNUM)) + (set (reg:HI HARD_X_REGNUM) + (mem:HI (plus:HI (reg:HI HARD_X_REGNUM) (const_int 2)))) + (set (match_dup 1) (reg:HI HARD_X_REGNUM))] + "") + + +;; Turn "and then test" into a "bit test" operation. +;; Provide variants for immediate and memory sources +;; This is the most used peephople. +; (define_peephole +; [(set (match_operand:QI 0 "register_operand" "=q") +; (and:QI (match_operand:QI 1 "register_operand" "0") +; (match_operand:QI 2 "immediate_operand" "i"))) +; (set (cc0) (match_dup 0))] +; "" +; "bit%0\t%2" +; [(set_attr "length" "3")]) +; +; (define_peephole +; [(set (match_operand:QI 0 "register_operand" "=q") +; (and:QI (match_operand:QI 1 "register_operand" "0") +; (match_operand:QI 2 "memory_operand" "m"))) +; (set (cc0) (match_dup 0))] +; "" +; "bit%0\t%2" +; [(set_attr "length" "4")]) + + +;; Turn a "decrement, then test" sequence into just a "decrement". +;; The test can be omitted, since it is implicitly done. +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (plus:QI (match_operand:QI 1 "whole_general_operand" "") + (match_operand:QI 2 "immediate_operand" ""))) + (set (cc0) (match_dup 0))] + "m6809_match_peephole2 (PEEP_DEC_IMPLIES_CC, PEEP_END)" + [(set (match_dup 0) (plus:QI (match_dup 1) (match_dup 2)))] + "") + + +;; Merge an indexed register increment with a previous usage. +;; This is usually done automatically, but not always +;; The 'use' should be optional; in all cases where this has been +;; seen, it is required though. +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (mem:QI (match_operand:HI 1 "index_register_operand" ""))) + (use (match_dup 0)) + (set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))] + "m6809_match_peephole2 (PEEP_INDEXED_INC, PEEP_END)" + [(set (match_dup 0) (mem:QI (post_inc:HI (match_dup 1)))) + (use (match_dup 0))] + "") + + +;;; Merge "ldX MEM; ldX ,X" into a single instruction using +;;; the indirect mode. +(define_peephole2 + [(set (reg:HI HARD_X_REGNUM) + (mem:HI (match_operand:HI 0 "general_operand" ""))) + (set (reg:HI HARD_X_REGNUM) (mem:HI (reg:HI HARD_X_REGNUM)))] + "reload_completed && m6809_match_peephole2 (PEEP_LDX_INDIRECT, PEEP_END)" + [(set (reg:HI HARD_X_REGNUM) + (mem:HI (mem:HI (match_dup 0))))] + "") + + +;;; Reorder a store followed by a unary operation on that memory +;;; so that the unary is performed and then the store. Consider +;;; a binary shift operation, which will be decomposed into +;;; identical single shifts, also. +;;; TODO - recognize more than just 'ashift' here. +(define_peephole2 + [(set (match_operand:QI 0 "memory_operand" "") + (match_operand:QI 1 "register_operand" "")) + (set (match_dup 0) + (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand")))] + "reload_completed" + [(set (match_dup 1) + (ashift:QI (match_dup 1) (match_operand:QI 2 "immediate_operand"))) + (set (match_dup 0) (match_dup 1))] + "") + +;;; Likewise, reorder a unary MEM followed by a load, so that the load +;;; is done first, then use the REG instead of the MEM. +;;;(define_peephole2 +;;; [(set (match_dup 0) +;;; (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand"))) +;;; (set (match_operand:QI 0 "register_operand" "") +;;; (match_operand:QI 1 "memory_operand" ""))] +;;; "reload_completed" +;;; [(set (match_dup 0) (match_dup 1)) +;;; (set (match_dup 0) +;;; (ashift:QI (match_dup 0) (match_operand:QI 2 "immediate_operand")))] +;;; "") + + +;;; Replace sex; leaX d,Y with leaX b,Y. +;;; +(define_peephole2 + [(set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM))) + (set (match_operand:HI 0 "index_register_operand" "") + (plus:HI (match_operand:HI 1 "index_register_operand" "") + (reg:HI HARD_D_REGNUM)))] + "reload_completed && m6809_match_peephole2 (PEEP_LEAB, PEEP_END)" + [(set (match_dup 0) + (plus:HI (match_dup 1) (reg:QI HARD_D_REGNUM)))] + "") + +(define_peephole2 + [(set (reg:HI HARD_D_REGNUM) (sign_extend:HI (reg:QI HARD_D_REGNUM))) + (set (match_operand:HI 0 "index_register_operand" "") + (plus:HI (reg:HI HARD_D_REGNUM) + (match_operand:HI 1 "index_register_operand" "")))] + "reload_completed && m6809_match_peephole2 (PEEP_LEAB, PEEP_END)" + [(set (match_dup 0) + (plus:HI (match_dup 1) (reg:QI HARD_D_REGNUM)))] + "") + + +;;; Replace ldb; decb; stb; tstb with dec(mem). If the +;;; register is not needed, then the load will get deleted +;;; automatically, but it may be needed for comparisons. +;;; Same for incb/inc. +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_dup 0) (plus:QI (match_dup 0) (const_int -1))) + (set (match_dup 1) (match_dup 0)) + (set (cc0) (match_dup 0))] + "m6809_match_peephole2 (PEEP_MEM_DEC_CMP, PEEP_END)" + [(set (match_dup 1) (plus:QI (match_dup 1) (const_int -1)))] + "") + + +;;; Replace ldb; decb; stb with dec(mem); ldb. If the +;;; register is not needed, then the load will get deleted +;;; automatically, but it may be needed for comparisons. +;;; Same for incb/inc. +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_dup 0) (plus:QI (match_dup 0) (const_int -1))) + (set (match_dup 1) (match_dup 0))] + "m6809_match_peephole2 (PEEP_MEM_DEC, PEEP_END)" + [(set (match_dup 1) (plus:QI (match_dup 1) (const_int -1))) + (set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_dup 0) (plus:QI (match_dup 0) (const_int 1))) + (set (match_dup 1) (match_dup 0))] + "m6809_match_peephole2 (PEEP_MEM_INC, PEEP_END)" + [(set (match_dup 1) (plus:QI (match_dup 1) (const_int 1))) + (set (match_dup 0) (match_dup 1))] + "") + + +;;; Replace "andb #N; cmpb #N; bhi" with "andb #N", if it can be proven +;;; that the branch can never occur because of the limited range of B. +;;; N must be a power of two for this to make sense. This helps with +;;; the default cases of switch statements on a value (x & N). +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (and:QI (match_dup 0) (match_operand:QI 1 "immediate_operand" ""))) + (set (cc0) + (compare (match_dup 0) (match_dup 1))) + (set (pc) (if_then_else (gtu (cc0) (const_int 0)) (match_operand 2 "" "") (match_operand 3 "" ""))) + ] + "reload_completed && power_of_two_p (INTVAL (operands[1]) + 1)" + [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1)))] + "") + +;;; Replace ldd <mem>; addd #1; std <mem> with 16-bit increment +;;; of the mem, but only if D is dead. Same for 16-bit decrement. +;;; <mem> must be offsettable for the instruction to match. +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") (match_operand:HI 1 "memory_operand" "")) + (set (match_dup 0) (plus:HI (match_dup 0) (const_int 1))) + (set (match_dup 1) (match_dup 0))] + "reload_completed + && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF + && peep2_reg_dead_p (3, operands[0])" + [(set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))] + "") + +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") (match_operand:HI 1 "memory_operand" "")) + (set (match_dup 0) (plus:HI (match_dup 0) (const_int -1))) + (set (match_dup 1) (match_dup 0))] + "reload_completed + && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF + && peep2_reg_dead_p (3, operands[0])" + [(set (match_dup 1) (plus:HI (match_dup 1) (const_int -1)))] + "") + + +;;; Replace a load or store using an indexed register, followed by an increment of that +;;; register, with the combined form using autoincrement. +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (mem:QI (match_operand:HI 1 "index_register_operand" ""))) + (set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))] + "reload_completed" + [(set (match_dup 0) (mem:QI (post_inc (match_dup 1))))] + "") + + +;;- mode:emacs-lisp +;;- comment-start: ";;- " +;;- eval: (set-syntax-table (copy-sequence (syntax-table))) +;;- eval: (modify-syntax-entry ?[ "(]") +;;- eval: (modify-syntax-entry ?] ")[") +;;- eval: (modify-syntax-entry ?{ "(}") +;;- eval: (modify-syntax-entry ?} "){") +;-; vim: set ts=2: +;-; vim: set expandtab: +;-; vim: set filetype=lisp: +;;- End: diff -urN gcc-4.6.4-clean/gcc/config/m6809/m6809.opt gcc-4.6.4/gcc/config/m6809/m6809.opt --- gcc-4.6.4-clean/gcc/config/m6809/m6809.opt 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/m6809.opt 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/config/m6809/m6809-protos.h gcc-4.6.4/gcc/config/m6809/m6809-protos.h --- gcc-4.6.4-clean/gcc/config/m6809/m6809-protos.h 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/m6809-protos.h 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/config/m6809/predicates.md gcc-4.6.4/gcc/config/m6809/predicates.md --- gcc-4.6.4-clean/gcc/config/m6809/predicates.md 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/predicates.md 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/config/m6809/t-coco gcc-4.6.4/gcc/config/m6809/t-coco --- gcc-4.6.4-clean/gcc/config/m6809/t-coco 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/t-coco 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/config/m6809/t-m6809 gcc-4.6.4/gcc/config/m6809/t-m6809 --- gcc-4.6.4-clean/gcc/config/m6809/t-m6809 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/t-m6809 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/config/m6809/t-sim gcc-4.6.4/gcc/config/m6809/t-sim --- gcc-4.6.4-clean/gcc/config/m6809/t-sim 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/gcc/config/m6809/t-sim 2017-11-28 21:12:11.156911596 -0700 @@ -0,0 +1 @@ +CRT0STUFF_T_CFLAGS += -DTARGET_SIM diff -urN gcc-4.6.4-clean/gcc/config.gcc gcc-4.6.4/gcc/config.gcc --- gcc-4.6.4-clean/gcc/config.gcc 2013-03-06 10:40:07.000000000 -0700 +++ gcc-4.6.4/gcc/config.gcc 2017-11-28 21:12:11.156911596 -0700 @@ -375,6 +375,9 @@ cpu_type=m32r extra_options="${extra_options} g.opt" ;; +m6809-*-*) + cpu_type=m6809 + ;; m68k-*-*) extra_headers=math-68881.h ;; @@ -1706,6 +1709,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.4-clean/gcc/gcse.c gcc-4.6.4/gcc/gcse.c --- gcc-4.6.4-clean/gcc/gcse.c 2011-02-02 23:04:04.000000000 -0700 +++ gcc-4.6.4/gcc/gcse.c 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/libgcc2.c gcc-4.6.4/gcc/libgcc2.c --- gcc-4.6.4-clean/gcc/libgcc2.c 2011-01-03 13:52:22.000000000 -0700 +++ gcc-4.6.4/gcc/libgcc2.c 2017-11-28 21:12:11.156911596 -0700 @@ -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.4-clean/gcc/longlong.h gcc-4.6.4/gcc/longlong.h --- gcc-4.6.4-clean/gcc/longlong.h 2011-10-04 01:28:50.000000000 -0600 +++ gcc-4.6.4/gcc/longlong.h 2017-11-28 21:12:11.160911575 -0700 @@ -528,6 +528,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.4-clean/gcc/Makefile.in gcc-4.6.4/gcc/Makefile.in --- gcc-4.6.4-clean/gcc/Makefile.in 2013-04-01 02:32:34.000000000 -0600 +++ gcc-4.6.4/gcc/Makefile.in 2017-11-28 21:12:11.160911575 -0700 @@ -2003,14 +2003,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.4-clean/gcc/opth-gen.awk gcc-4.6.4/gcc/opth-gen.awk --- gcc-4.6.4-clean/gcc/opth-gen.awk 2011-02-08 10:41:00.000000000 -0700 +++ gcc-4.6.4/gcc/opth-gen.awk 2017-11-28 21:12:11.160911575 -0700 @@ -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.4-clean/gcc/tree.h gcc-4.6.4/gcc/tree.h --- gcc-4.6.4-clean/gcc/tree.h 2011-10-06 13:57:52.000000000 -0600 +++ gcc-4.6.4/gcc/tree.h 2017-11-28 21:12:11.160911575 -0700 @@ -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.4-clean/gcc/version.c gcc-4.6.4/gcc/version.c --- gcc-4.6.4-clean/gcc/version.c 2009-04-21 13:03:23.000000000 -0600 +++ gcc-4.6.4/gcc/version.c 2017-11-28 21:20:44.918095673 -0700 @@ -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 pl8)"; const char pkgversion_string[] = PKGVERSION; diff -urN gcc-4.6.4-clean/libgcc/config.host gcc-4.6.4/libgcc/config.host --- gcc-4.6.4-clean/libgcc/config.host 2011-11-23 15:15:54.000000000 -0700 +++ gcc-4.6.4/libgcc/config.host 2017-11-28 21:12:11.160911575 -0700 @@ -371,6 +371,8 @@ ;; m32rle-*-linux*) ;; +m6809*) + ;; m68hc11-*-*|m6811-*-*) ;; m68hc12-*-*|m6812-*-*) diff -urN gcc-4.6.4-clean/libgcc/fixed-obj.mk gcc-4.6.4/libgcc/fixed-obj.mk --- gcc-4.6.4-clean/libgcc/fixed-obj.mk 2007-09-17 16:18:13.000000000 -0600 +++ gcc-4.6.4/libgcc/fixed-obj.mk 2017-11-28 21:12:11.160911575 -0700 @@ -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.4-clean/libgcc/Makefile.in gcc-4.6.4/libgcc/Makefile.in --- gcc-4.6.4-clean/libgcc/Makefile.in 2012-12-04 12:11:33.000000000 -0700 +++ gcc-4.6.4/libgcc/Makefile.in 2017-11-28 21:12:11.160911575 -0700 @@ -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.4-clean/README.LW gcc-4.6.4/README.LW --- gcc-4.6.4-clean/README.LW 1969-12-31 17:00:00.000000000 -0700 +++ gcc-4.6.4/README.LW 2017-11-28 21:12:11.160911575 -0700 @@ -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!