diff options
Diffstat (limited to 'gcc/config/z8k/z8k.c')
-rwxr-xr-x | gcc/config/z8k/z8k.c | 2910 |
1 files changed, 2910 insertions, 0 deletions
diff --git a/gcc/config/z8k/z8k.c b/gcc/config/z8k/z8k.c new file mode 100755 index 0000000..477c97e --- /dev/null +++ b/gcc/config/z8k/z8k.c @@ -0,0 +1,2910 @@ +/* Subroutines for insn-output.c for the Zilog Z8000 + Copyright (C) 1993, 1994 Free Software Foundation, Inc. + + Written by Steve Chamberlain (sac@cygnus.com) + + This file is part of GNU CC. + + GNU CC 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. + + GNU CC 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 GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "tree.h" +#include "flags.h" + +/* Modes ok for regs, used in tm.h for HARD_REGNO_MODE_OK */ +int hard_regno_mode_ok[FIRST_PSEUDO_REGISTER]; + + +/* We can change which reg has the sp or fp, using this + to indirect */ +static int renumber[FIRST_PSEUDO_REGISTER]; +static int rev_renumber[FIRST_PSEUDO_REGISTER]; + + +/* If these are ever reversed, then things will have to change, call.c + assumes that args go in regs in increasing regno order */ + +#define ARG_REGS "r2,r3,r4,r5,r6,r7" /* Default -margs-in */ + +/* Number of HI regs used for arg passing, can be changed on the + command line (-margs-in) */ + +int arg_nregs; + +/* Regs for arg passing */ +int arg_regs[FIRST_PSEUDO_REGISTER]; + +/* And their order */ +int arg_regs_order[FIRST_PSEUDO_REGISTER]; + +extern int frame_pointer_needed; +#define pic_reg (gen_rtx(REG, HImode, 13)) + +struct reg_info_struct + { + char *mode_name[MAX_MACHINE_MODE]; + }; + +struct reg_info_struct rinfo[FIRST_PSEUDO_REGISTER]; + +#define regname(x,mode) (rinfo[renumber[x]].mode_name[mode]) + +char *pointer_reg[FIRST_PSEUDO_REGISTER]; + +#define STACK_REGISTER(x) \ + ((x)==STACK_POINTER_REGNUM || (frame_pointer_needed && (x) == FRAME_POINTER_REGNUM)) + +/* kludge - used if insn does ba bx set in ASM_OUTPUT_OPCODE if the + insn allows ba, bx addressing modes */ + +static int can_ba_bx; + +int current_function_anonymous_args; + +/* Number of bytes pushed for anonymous args */ + +static int extra_push; + +/* Linked list of all externals that are to be emitted if they haven't been + declared by the end of the program. */ + +struct extern_list + { + struct extern_list *next; + char *name; + } + *extern_head = 0; + +rtx +getreg (x) + rtx x; +{ + if (GET_CODE (x) == TRUNCATE) + x = XEXP (x, 0); + if (GET_CODE (x) == SIGN_EXTEND) + x = XEXP (x, 0); + return x; +} + +/* Print the operand address represented by the rtx addr */ + +void +print_operand_address (file, addr) + FILE *file; + register rtx addr; +{ + rtx lhs, rhs; + +retry:; + + switch (GET_CODE (addr)) + { + case MEM: + fprintf (file, "#"); + addr = XEXP (addr, 0); + goto retry; + + case REG: + fprintf (file, "@%s", pointer_reg[REGNO (addr)]); + break; + + case PRE_DEC: + fprintf (file, "%s", pointer_reg[REGNO (XEXP (addr, 0))]); + break; + case POST_INC: + fprintf (file, "%s", pointer_reg[REGNO (XEXP (addr, 0))]); + break; + case PLUS: + lhs = XEXP (addr, 0); + rhs = XEXP (addr, 1); + + if (GET_CODE (rhs) == REG + && GET_CODE (lhs) != REG) + { + rtx swap = lhs; + lhs = rhs; + rhs = swap; + } + + + if (GET_CODE (rhs) == SIGN_EXTEND + || GET_CODE (rhs) == TRUNCATE) + { + rtx swap = lhs; + lhs = rhs; + rhs = swap; + } + + if (TARGET_BIG && (inside_ba_p (addr, 1) + && (STACK_REGISTER (REGNO (lhs)) || can_ba_bx) + && GET_CODE (rhs) == CONST_INT)) + { + int offset = INTVAL (rhs); + if (REGNO (lhs) == STACK_POINTER_REGNUM && saved_reg_on_stack_hack) + offset += 4; + saved_reg_on_stack_hack = 0; + fprintf (file, "%s(#", pointer_reg[REGNO (lhs)]); + fprintf (file, "%d", offset); + fprintf (file, ")"); + } + else if (inside_bx_p (addr, 1) + && (can_ba_bx || (STACK_REGISTER (REGNO (lhs))))) + { + rtx base; + rtx disp; + + if (GET_CODE (lhs) == REG) + { + base = lhs; + disp = rhs; + } + else + { + base = rhs; + disp = lhs; + } + fprintf (file, "%s(%s)", pointer_reg[REGNO (base)], + regname(REGNO (getreg (disp)), HImode)); + } + else if (inside_x_p (addr, 1)) + { + int r = REGNO (lhs); + + if (TARGET_BIG) + { + /* With a disp(reg) we only use the lsw, so + inc the reg number */ + if (GET_MODE (lhs) == SImode || GET_MODE (lhs) == PSImode) + r++; + } + output_address (rhs); + fprintf (file, "(%s)", regname(r,HImode)); + } + else + { + /* This must be an x_operand or a stack reg */ + output_address (rhs); + fprintf (file, "(%s)", regname(REGNO (lhs),HImode)); + + } + break; + + default: + output_addr_const (file, addr); + + } +} + + + +void +maybe_need_resflg (cond) + rtx cond; +{ + int code = GET_CODE (cond); + if (cc_prev_status.flags & CC_NO_OVERFLOW + && (code == GT + || code == LE)) + { + fprintf (asm_out_file, "\tresflg v\n"); + } +} + +/* Turn a condition code into a string */ +static char * +cond_name_x (code) + int code; +{ + if (cc_prev_status.flags & CC_NO_OVERFLOW) + { + switch (code) + { + case LT: + return "mi"; + case GE: + return "pl"; + /* The others V testers are handled by clearing V before the jump */ + } + } + switch (code) + { + case EQ: + return "eq"; + case NE: + return "ne"; + case LT: + return "lt"; + case LE: + return "le"; + case GT: + return "gt"; + case GE: + return "ge"; + case LTU: + return "ult"; + case LEU: + return "ule"; + case GTU: + return "ugt"; + case GEU: + return "uge"; + default: + abort (); + } +} + + +/* print either an inc or an add depending upon the size of the value */ + +static void +incordec (file, n1, n2, size) + FILE *file; + char *n1; + char *n2; + int size; +{ + if (size) + { + fprintf (file, "\t%s\tr15,#%d\n", + size > 16 ? n1 : n2, size); + } +} + + +#define frameish(x) ((x == FRAME_POINTER_REGNUM) || (x == FRAME_POINTER_REGNUM+1)) + +/* return 1 if the register needs to be saved on function entry */ + +static int +need (regno) + int regno; +{ + if (TARGET_BIG && regno == STACK_POINTER_REGNUM) + return 0; + + return (regs_ever_live[regno] + && !call_used_regs[regno] + && regno < STACK_POINTER_REGNUM + && !((regno == FRAME_POINTER_REGNUM) + || (regno == FRAME_POINTER_REGNUM + 1) && frame_pointer_needed)); + +} + + + + +/* return non zero if the rtx supplied can be used as an effective + address calculation */ + +static int +address (op) + rtx op; +{ + if (GET_CODE (op) == CONST) + op = XEXP (op, 0); + + if (TARGET_SMALL && GET_CODE (op) == CONST_INT) + return 1; + + if (GET_MODE (op) != Pmode) + return 0; + + /* + (symbol_ref, foo) is address */ + if (GET_CODE (op) == PLUS && address (XEXP (op, 0))) + return 1; + + if (GET_CODE (op) == SYMBOL_REF) + return 1; + + if (GET_CODE (op) == LABEL_REF) + return 1; + + return 0; +} + +/* +Use S for SI regs +Use B & T for parts of DI regs + X - stack pointer name + Registers + Q - byte sized register name + U - high byte of word register + V - low byte of word register + H - word register name + I - next word register name + S&B - long register name + T - next long register name + D - quad register name + P - register name in size of pointer + Integers + O - log two of value + P - inverted log two + H - bottom 16 bits + I - top 16 bits + N - negative + B - high 32 bits of 32bit number. + default: value + Memory + I - adjusted upwards by two + T - adjusted upwards by four + default: value + Address + H - low 16 bits + I - high 16 bits + A - as long constant + S - as A but with # + default: error + Misc + C - conditional name + D - reverse conditional name + F - clear v flag if necessary + */ + + +void +print_operand (file, x, code) + FILE *file; + rtx x; + int code; +{ + if (code == '^') + { + static int lab; + fprintf (file, "%d", lab >> 1); + lab++; + } + else if (code == 'F') + { + if (cc_prev_status.flags & CC_NO_OVERFLOW) + { + fprintf (file, "resflg v\n\t"); + } + } + else if (code == 'X') + { + fprintf (file, "%s", pointer_reg[STACK_POINTER_REGNUM]); + } + else + { + /* If reg, output byte reg */ + if (GET_CODE (x) == REG) + { + switch (code) + { + case 'Q': + fprintf (file, "%s", regname (REGNO (x), QImode)); + break; + case 'U': + fprintf (file, "rh%d", REGNO (x)); + break; + case 'V': + fprintf (file, "rl%d", REGNO (x) + 1); + break; + + default: + fprintf (file, "r??%d", REGNO (x)); + break; + case 'H': + fprintf (file, "%s", regname (REGNO (x), HImode)); + break; + case 'I': + fprintf (file, "%s", regname (REGNO (x) + 1, HImode)); + break; + case 'J': + fprintf (file, "%s", regname (REGNO (x) + 2, HImode)); + break; + case 'K': + fprintf (file, "%s", regname (REGNO (x) + 3, HImode)); + break; + case 'S': + case 'B': + fprintf (file, "%s", regname (REGNO (x), SImode)); + break; + case 'T': + fprintf (file, "%s", regname (REGNO (x) + 2, SImode)); + break; + case 'D': + fprintf (file, "%s", regname (REGNO (x), DImode)); + break; + case 'P': + fprintf (file, "%s", regname (REGNO (x), Pmode)); + break; + } + + } + + else if (GET_CODE (x) == CONST_INT) + { + switch (code) + { + case 'O': + fprintf (file, "#%d", exact_log2 (INTVAL (x))); + break; + case 'P': + fprintf (file, "#%d", exact_log2 (~(INTVAL (x) | ~0xffff))); + break; + case 'H': + fprintf (file, "#%d", INTVAL (x) & 0xffff); + break; + case 'I': + fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xffff); + break; + case 'N': + fprintf (file, "#%d", -INTVAL (x)); + break; + case 'B': + { + HOST_WIDE_INT val = INTVAL (x); + if (sizeof (val) == 4) + val = val < 0 ? -1 : 0; + else { + /* only happens with 32 bit values, so even when + HOST_WIDE_INT is 32 bits long this is no error */ + val >>= 32; + } + fprintf (file, "#%d", val); + } + break; + default: + fprintf (file, "#%d", INTVAL (x) & 0xffffffff); + break; + } + } + else if (GET_CODE (x) == MEM) + { + if (code == 'I') + { + x = adj_offsettable_operand (x, 2); + } + else if (code == 'T') + { + x = adj_offsettable_operand (x, 4); + } + + output_address (XEXP (x, 0)); + + } + else if (address (x)) + { + switch (code) + { + case 'H': + if (TARGET_BIG) + { + fprintf (file, "#low("); + output_addr_const (file, x); + fprintf (file, ")"); + } + else + { + fprintf (file, "#"); + output_addr_const (file, x); + } + break; + + case 'I': + fprintf (file, "#high("); + output_addr_const (file, x); + fprintf (file, ")"); + break; + case 'A': + output_addr_const (file, x); + break; + case 'S': + fprintf (file, "#"); + output_addr_const (file, x); + break; + default: + fprintf (file, "#what%c", code); + output_addr_const (file, x); + break; + } + } + else if (code == 'C') + { + fprintf (file, cond_name_x (reverse_condition (GET_CODE (x)))); + } + else if (code == 'D') + { + fprintf (file, cond_name_x (GET_CODE (x))); + } + else if (GET_CODE (x) == CONST_DOUBLE) + { + double d; + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + fprintf (file, "#"); + ASM_OUTPUT_FLOAT (file, d); + } + else + { + fprintf (file, "#"); + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + if (GET_CODE (x) == TRUNCATE) + x = XEXP (x, 0); + output_addr_const (file, x); + } + } +} + + +static +int +fill_from_options (value, def, string, into, order) + int value; + char *def; + char *string; + char *into; + int *order; +{ + int regno; + int n = 0; + char *p; + char *list; + + list = p = string ? string : def; + + while (*p) + { + if (*p == 'r') + { + p++; + if (p[0] == '1' && p[1] >= '0' && p[1] <= '5') + { + regno = 10 + p[1] - '0'; + p += 2; + } + else if (p[0] >= '0' && p[0] <= '9') + { + regno = p[0] - '0'; + p++; + } + else + { + error ("error in register list `%s'", list); + return 0; + } + into[regno] = value; + if (order) + { + order[n] = regno; + } + n++; + + } + else if (*p == ',' || *p == '-') + { + p++; + } + else + { + error ("error in register list `%s'", list); + return 0; + } + } + return n; +} +extern enum debug_info_type write_symbols; +extern enum debug_info_level debug_info_level; +extern int flag_caller_saves; + +void +override_options () +{ + int i; + int j; + + int nfakes; + + + if (TARGET_STD) + { + target_flags |= TARGET_STD_RET_BIT | TARGET_STD_FRAME_BIT; + target_flags &= ~TARGET_REGPARMS_BIT; + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + renumber[i] = i; + + if (TARGET_BIG) + pmode = PSImode; + else + pmode = HImode; + if (TARGET_STD_FRAME) + flag_omit_frame_pointer = 0; + + + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int mode_index; + + for (mode_index = 0; + mode_index <= (int) MAX_MACHINE_MODE; + mode_index++) + { + int bsize = GET_MODE_UNIT_SIZE ((enum machine_mode) mode_index); + int or; + + if (bsize > 8) + or = 0; + else if (bsize == 8 && (i & 1)) + or = 0; + else if (bsize == 4 && (i & 1)) + or = 0; + else + or = 1; + + hard_regno_mode_ok[i] |= or << mode_index; + } + } + + /* Call used - current scheme is all registers are trashed */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + call_used_regs[i] = 1; + + /* except for ones which are explicitly mention */ + if (TARGET_BIG) + { + fill_from_options (0, "r8,r9,r10,r11,r12,r13", call_used_option, call_used_regs, 0); + } + else + { + fill_from_options (0, "r8,r9,r10,r11,r12,r13,r14", call_used_option, call_used_regs, 0); + } + + + arg_nregs = fill_from_options (1, ARG_REGS, args_in_option, + arg_regs, + arg_regs_order); + + if (fakes_option) + { + nfakes = atoi (fakes_option); + } + else + nfakes = 0; + + /* Make fixed those which aren't going to be fake */ + for (j = 0, i = STACK_POINTER_REGNUM + 1; + i < FIRST_PSEUDO_REGISTER; i++) + { + fixed_regs[i] = !(j < nfakes); + } + + /* The stack and frame pointer shouldn't be call_used */ + + call_used_regs[STACK_POINTER_REGNUM] = 1; + call_used_regs[FRAME_POINTER_REGNUM] = 0; + + if (TARGET_BIG) + { + call_used_regs[STACK_POINTER_REGNUM + 1] = 1; + call_used_regs[FRAME_POINTER_REGNUM + 1] = 0; + } + + /* Fill in the ok modes */ + + /* Work out the names of the registers */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + + regname (i, QImode) = strdup (" "); + sprintf (regname (i, QImode), "*QI%d", i); + regname (i, HImode) = strdup (" "); + sprintf (regname (i, HImode), "*HI%d", i); + regname (i, SImode) = strdup (" "); + sprintf (regname (i, SImode), "*SI%d", i); + regname (i, DImode) = strdup (" "); + sprintf (regname (i, DImode), "*DI%d", i); + } + + regname (0, QImode) = "rl0"; + regname (1, QImode) = "rl1"; + regname (2, QImode) = "rl2"; + regname (3, QImode) = "rl3"; + regname (4, QImode) = "rl4"; + regname (5, QImode) = "rl5"; + regname (6, QImode) = "rl6"; + regname (7, QImode) = "rl7"; + + regname (0, HImode) = "r0"; + regname (1, HImode) = "r1"; + regname (2, HImode) = "r2"; + regname (3, HImode) = "r3"; + regname (4, HImode) = "r4"; + regname (5, HImode) = "r5"; + regname (6, HImode) = "r6"; + regname (7, HImode) = "r7"; + regname (8, HImode) = "r8"; + regname (9, HImode) = "r9"; + regname (10, HImode) = "r10"; + regname (11, HImode) = "r11"; + regname (12, HImode) = "r12"; + regname (13, HImode) = "r13"; + regname (14, HImode) = "r14"; + regname (15, HImode) = "r15"; + + regname (0, SImode) = "rr0"; + regname (2, SImode) = "rr2"; + regname (4, SImode) = "rr4"; + regname (6, SImode) = "rr6"; + regname (8, SImode) = "rr8"; + regname (10, SImode) = "rr10"; + regname (12, SImode) = "rr12"; + regname (14, SImode) = "rr14"; + + regname (0, PSImode) = "rr0"; + regname (2, PSImode) = "rr2"; + regname (4, PSImode) = "rr4"; + regname (6, PSImode) = "rr6"; + regname (8, PSImode) = "rr8"; + regname (10, PSImode) = "rr10"; + regname (12, PSImode) = "rr12"; + regname (14, PSImode) = "rr14"; + + if (TARGET_BIG) + { + /* We need r11 and r15 for big mode */ + fixed_regs[15] = 1; + fixed_regs[11] = 1; + call_used_regs[11] = 1; + } + else + { + /* for little mode we use 15 as the sp */ + renumber[15] = 14; + renumber[14] = 15; + } + + + /* The frame pointer is odd too. When in STD_FRAME mode we keep the + frame pointer inside GCC in r10, but we print it out as rr12 or r14.. */ + + if (TARGET_STD_FRAME) + { + if (TARGET_BIG) { + renumber[10] = 12; + renumber[11] = 13; + renumber[12] = 10; + renumber[13] = 11; + } + else { + /* So the internal fp at 10 comes out in r14, + the interal sp comes out at 15 + and r15 is printed as r10 */ + renumber[FRAME_POINTER_REGNUM] = 14; + renumber[STACK_POINTER_REGNUM] = 15; + renumber[15] = 10; + } + } + + regname (0, DImode) = "rq0"; + regname (4, DImode) = "rq4"; + regname (8, DImode) = "rq8"; + regname (12, DImode) = "rq12"; + + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + pointer_reg[i] = regname (i, Pmode); + + if (TARGET_DEFS || TARGET_SOURCE || TARGET_LINE) + { + write_symbols = SDB_DEBUG; + debug_info_level = DINFO_LEVEL_NORMAL; + } + + if (TARGET_PIC) + { + fixed_regs[13] = 1; + } + flag_force_mem = 0; + flag_caller_saves = 0; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + rev_renumber[renumber[i]] = i; + +} + + +/* + Offset from the stack pointer register to the first location at + which outgoing arguments are placed. If not specified, the + default value of zero is used. This is the proper value for most + machines. + */ + + + +rtx +z8k_function_arg (cum, mode, type, named) + int cum; + enum machine_mode mode; + tree type; + int named; +{ + int t; + int nregs; + int rn; + enum machine_mode rmode = ((mode == BLKmode) ? TYPE_MODE (type) : mode); + + if (mode == BLKmode || mode == VOIDmode) + return 0; + else + nregs = (GET_MODE_SIZE (mode) + 1) / 2; + + if (!TARGET_REGPARMS) + { + /* Might not be allowed to do this */ + return 0; + } + + /* Varargs always go on stack, unless in YASM mode*/ + if (!TARGET_YASM) + { + if (!named) + return 0; + } + + /* Never put a struct in regs - one reason is that calls.c doesn't + know if you load your regs backwards or forwards. We could fix + this by printing r0 as r15, r1 as r14 etc + */ + + /* Move down till we have used enough regs and + we're aligned */ + + rn = cum - (nregs - 1); + while (rn >= 2 + && !HARD_REGNO_MODE_OK (rn, rmode)) + { + rn--; + } + + + if (rn < 2 || rn > 7) + return 0; + return gen_rtx (REG, rmode, rn); +} + +/* return 1 if there isn't anything tricky to do */ + +int +null_epilogue () +{ + int i; + + if (!reload_completed) + return 0; + if (frame_pointer_needed) + return 0; + if (get_frame_size ()) + return 0; + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (regs_ever_live[i] && !call_used_regs[i]) + return 0; + } + + return 1; +} + +struct rtx_def * +z8k_builtin_saveregs (arglist) + tree arglist; +{ + int i; + + for (i = 0; i < arg_nregs; i++) + { + emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, HImode, arg_regs_order[i]))); + } + return 0; +} + +void +asm_output_local (file, name, size, rounded) + FILE *file; + char *name; + int size; + int rounded; +{ + if (TARGET_YASM) + { + tree name_tree = get_identifier (name); + TREE_ASM_WRITTEN (name_tree) = 1; + } + + data_section (); + assemble_name (file, name); + fprintf (file, ":\n\tblock\t%u\n", rounded); +} + +void +asm_output_common (file, name, size, rounded) + FILE *file; + char *name; + int size; + int rounded; +{ + if (TARGET_YASM) + { + data_section(); + if (size > 1) + { + ASM_OUTPUT_ALIGN (file, 1); + } + ASM_GLOBALIZE_LABEL (file, name); + ASM_OUTPUT_LOCAL (file, name, size, rounded); + } + else + { + fputs (".comm ", file); + assemble_name (file, name); + fprintf (file, ",%u\n", rounded); + } +} + +void +asm_output_name (file, name) + FILE *file; + char *name; +{ + if (TARGET_YASM) + { + tree name_tree = get_identifier (name); + TREE_ASM_WRITTEN (name_tree) = 1; + } + + ASM_OUTPUT_LABEL (asm_out_file, name); +} + +/* + emit instructions to move operands[1] to operands[0]. + + Return 1 if we've done everything that needs to be done, otherwise + return 0 and let the compiler emit a move instruction using possibly + altered operands. +*/ +extern rtx copy_to_mode_reg (); +rtx +simple (mode, operand) + enum machine_mode mode; + rtx operand; +{ + + if (mode == DFmode + && GET_CODE (operand) == MEM && + GET_CODE (XEXP (operand, 0)) == PLUS) + { + /* Operand 0 of the PLUS must be a register, so we can force operand 1 + into a new pseudo reg, and then safely add operand 0 to it. */ + rtx x = XEXP (operand, 0); + rtx t1 = XEXP (x, 0); + rtx t2 = copy_to_mode_reg (Pmode, XEXP (x, 1)); + emit_insn (gen_rtx (SET, VOIDmode, t2, + gen_rtx (PLUS, Pmode, t2, t1))); + return gen_rtx (MEM, mode, t2); + } + + return operand; +} + +int +emit_move (operands, mode, extra) + rtx operands[]; + enum machine_mode mode; + int extra; + +{ + extern int reload_in_progress; + rtx operand0 = operands[0]; + rtx operand1 = operands[1]; + int need_copy = 0; + + if (rtx_equal_p (operand0, operand1)) + return 1; + if (!reload_in_progress) + { + /* Can't push a long immediate */ + if (mode == SImode + && push_operand (operand0, mode) + && immediate_operand (operand1, mode)) + { + need_copy = 1; + } + + if (mode == SFmode + && immediate_operand (operand1, mode)) + { + need_copy = 1; + } + + if (push_operand (operand0, mode)) + { + need_copy = + !ir_operand (operand1, mode) + && !da_operand (operand1, mode) + && !x_operand (operand1, mode); + { + need_copy = 1; + } + } + if (!register_operand (operand0, mode) + && !register_operand (operand1, mode)) + { + need_copy = 1; + } + + } + if (TARGET_BIG && !reload_in_progress) + { + operand1 = simple (mode, operand1); + operand0 = simple (mode, operand0); + } + if (need_copy) + { + rtx temp = gen_reg_rtx (mode); + emit_insn (gen_rtx (SET, VOIDmode, temp, operand1)); + emit_insn (gen_rtx (SET, VOIDmode, operand0, temp)); + return 1; + } + operands[0] = operand0; + operands[1] = operand1; + return 0; +} + +int +r_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (BADSUBREG (op)) + return 0; + return register_operand (op, mode); +} + +int +ir_operand_s (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != GET_MODE (op)) + return 0; + if (BADSUBREG (op)) + return 0; + return IR_P (op); +} + +int +ir_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return ir_operand_s (op, mode); +} + +int +da_operand_s (op, mode, strict) + rtx op; + enum machine_mode mode; + int strict; +{ + if (mode != GET_MODE (op)) + return 0; + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + return DA_P (op); +} + +int +da_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return da_operand_s (op, mode, reload_in_progress); +} + +/* 16 bit Reg + pointer sized bit index*/ + +x_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (BADSUBREG (op)) + return 0; + if (mode != GET_MODE (op)) + return 0; + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + return X_P (op); +} + + +int +find_reg (op, strict) + rtx op; + int strict; +{ + int rn = REGNO (op); + if (strict && reg_renumber == 0) + abort (); + if (strict && rn >= FIRST_PSEUDO_REGISTER) + return reg_renumber[rn]; + return rn; +} + +ok_for_base (op, strict) + rtx op; + int strict; +{ + int rn; + if (GET_CODE (op) != REG) + return 0; + if (GET_MODE (op) != Pmode) + return 0; + rn = find_reg (op, strict); + + if (reload_in_progress && rn > FIRST_PSEUDO_REGISTER) + return 1; + + if (strict) + { + if (rn <= 0) + return 0; + return 1; + } + else + { + return rn != 0; + } +} + +ok_for_index (op, strict) + rtx op; + int strict; +{ + int rn; + if (GET_CODE (op) != REG) + return 0; + if (GET_MODE (op) != HImode + && GET_MODE (op) != Pmode) + return 0; + rn = find_reg (op, strict); + if (strict) + { + if (rn <= 0) + return 0; + return 1; + } + else + { + return rn != 0; + } +} + + +inside_bx_p (op, strict) + rtx op; +{ + if (GET_CODE (op) == PLUS) + { + rtx lhs = XEXP (op, 0); + rtx rhs = XEXP (op, 1); + if (ok_for_base (rhs, strict)) + { + if (GET_CODE (lhs) == TRUNCATE) + lhs = XEXP (lhs, 0); + if (GET_CODE (lhs) == SIGN_EXTEND) + { + lhs = XEXP (lhs, 0); + if (ok_for_index (lhs, strict)) + return 1; + } + } + + if (ok_for_base (lhs, strict)) + { + if (GET_CODE (rhs) == TRUNCATE) + rhs = XEXP (rhs, 0); + if (GET_CODE (rhs) == SIGN_EXTEND) + { + rhs = XEXP (rhs, 0); + if (ok_for_index (rhs, strict)) + return 1; + } + } + } + return 0; +} + + +/* 16 bit register + pointer sized address */ +inside_x_p (op, strict) + rtx op; + +{ + if (strict != 0 && strict != 1) + abort (); + if (GET_CODE (op) == PLUS) + { + rtx lhs = XEXP (op, 0); + rtx rhs = XEXP (op, 1); + if (TARGET_SMALL) + { + if (GET_CODE (lhs) == CONST_INT && ok_for_index (rhs, strict)) + return 1; + + if (GET_CODE (rhs) == CONST_INT && ok_for_index (lhs, strict)) + return 1; + } + + if (data_ref_p (lhs)) + { + + if (ok_for_index (rhs, strict)) + { + return 1; + } + } + + if (data_ref_p (rhs)) + { + if (ok_for_index (lhs, strict)) + { + return 1; + } + } + + } + return 0; +} + + +inside_da_p (op, strict) + rtx op; + int strict; +{ + if (DATA_REF_P (op)) + return 1; + return 0; +} + +inside_ba_p (op, strict) + rtx op; +{ + if (strict != 0 && strict != 1) + abort (); + if (GET_CODE (op) == PLUS) + { + rtx lhs = XEXP (op, 0); + rtx rhs = XEXP (op, 1); + if (ok_for_base (lhs, strict)) + { + if (GET_CODE (rhs) == CONST_INT) + { + if (INTVAL (rhs) & 1) + return 0; + return 1; + } + } + } + return 0; +} + +bx_p (op, strict) + rtx op; +{ + if (strict != 0 && strict != 1) + abort (); + if (GET_CODE (op) != MEM) + return 0; + return inside_bx_p (XEXP (op, 0), strict); +} + +ba_p (op, strict) + rtx op; +{ + if (strict != 0 && strict != 1) + abort (); + if (GET_CODE (op) != MEM) + return 0; + return inside_ba_p (XEXP (op, 0), strict); +} + +x_p (op, strict) + rtx op; +{ + if (GET_CODE (op) != MEM) + return 0; + return inside_x_p (XEXP (op, 0), 0); +} + + +int +ba_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (BADSUBREG (op)) + return 0; + if (mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (ba_p (op, 0)) + return 1; + return 0; +} + +int +bx_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (BADSUBREG (op)) + return 0; + + if (bx_p (op, 0)) + return 1; + return 0; +} + +int +im_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return immediate_operand (op, mode); +} + +int +r_ir_da_x_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode); + return r; + +} + + +/* When operating on a DI or DF we'll always + need to get to another double word. This makes + the ir mode not work (since 4(rn) is invalid unless + bx can also be done). This works on the Z8002 since + x mode will suffice */ +int +r_ir_da_x_operand_for_di (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || (TARGET_SMALL && ir_operand (op, mode)) + || da_operand (op, mode) + || (TARGET_SMALL && x_operand (op, mode)); + return r; + +} + +int +r_im_ir_da_x_operand_for_di (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || im_operand (op, mode) + || (TARGET_SMALL && ir_operand (op, mode)) + || da_operand (op, mode) + || (TARGET_SMALL && x_operand (op, mode)); + return r; + +} + + + +int +r_ir_da_x_ba_operand_for_di (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ba_operand (op, mode) + || ir_operand (op, mode) /* Note we can use this because ba is here */ + || da_operand (op, mode) + || x_operand (op, mode); + return r; + +} + + + +int +r_ir_da_x_ba_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode); + return r; + +} + +int +r_im_ir_da_x_ba_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || im_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode); + return r; + +} + +int +r_im_ir_da_x_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || im_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode); + return r; +} + + +int +r_im_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return r_operand (op, mode) || im_operand (op, mode); +} + +int +r_im_ir_da_x_ba_bx_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || im_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode); + return r; +} + +int +r_ir_da_x_ba_bx_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode); + return r; +} + +int +r_ir_da_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode); + return r; +} + +int +move_check (operands, mode) + rtx operands[]; + enum machine_mode mode; +{ + return 1; +} + +int +register_move_cost (x, y) + int x; + int y; +{ + if ((x == QI_REGS) != (y == QI_REGS)) + return 5; + + return 2; +} + +int +r_im_ir_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || im_operand (op, mode) + || ir_operand (op, mode); + return r; +} + +int +r_im_ir_da_x_operand_or_r (op, mode) + rtx op; + enum machine_mode mode; +{ + return (TARGET_BIG) ? r_operand (op, mode) : r_im_ir_da_x_operand (op, mode); +} + +int +r_da_x_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return r_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode); +} + +int +r_im_ir_da_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return r_operand (op, mode) + || im_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode); + + +} + +ir_da_x_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode); +} + +char * +z8k_asm_output_opcode (file, string) + FILE *file; + char *string; +{ + if (string[0] == '$') + { + can_ba_bx = 1; + string++; + } + else + { + can_ba_bx = 0; + } + return string; +} + + +int +data_ref_p_1 (X) + rtx X; +{ + if (TARGET_PIC) + return 0; + + return (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF + || (GET_CODE (X) == PLUS + && GET_CODE (XEXP (X, 0)) == SYMBOL_REF + && GET_CODE (XEXP (X, 1)) == CONST_INT)); +} + +int +data_ref_p (X) + rtx X; +{ + if (TARGET_PIC) + return 0; + if (TARGET_BIG) + return (DATA_REF_P_1 (X) || (GET_CODE (X) == CONST && DATA_REF_P_1 (XEXP (X, 0)))); + return CONSTANT_P (X); +} + + + +int +disp_p (X) + rtx X; +{ + return + ((GET_CODE (X) == CONST_INT && (((unsigned) INTVAL (X) + 0xffff) < 0x1ffff)) || (!TARGET_HUGE && DATA_REF_P (X))); +} + + +int +ptr_reg (x) + rtx x; +{ + if (reload_completed || !TARGET_HUGE) + return 1; + if (STACK_REGISTER (REGNO (x))) + return 1; + return 0; +} + + +/* Work out the registers which need to be saved, both as a mask and a + count */ + +int +calc_live_regs (count) + int *count; +{ + int reg; + int live_regs_mask = 0; + *count = 0; + + for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) + { + if (regs_ever_live[reg] && !call_used_regs[reg]) + { + (*count)++; + live_regs_mask |= (1 << reg); + + if (reg == FRAME_POINTER_REGNUM && frame_pointer_needed && TARGET_BIG) + { + /* Count r11 too */ + (*count)++; + reg++; + live_regs_mask |= (1 << reg); + } + } + } + return live_regs_mask; +} + +io (from, to) +{ + int regs_saved; + int d = calc_live_regs (®s_saved); + int total_saved_regs_space = (regs_saved) * 2; + int total_auto_space = get_frame_size (); + int pcsize = TARGET_BIG ? 4 : 2; + + if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + { + return total_saved_regs_space + pcsize; + } + + if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + { + return total_saved_regs_space + total_auto_space + pcsize; + } + + if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + { + return total_auto_space; + } + + if (from == RETURN_ADDRESS_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + { + return total_saved_regs_space; + } + + if (from == RETURN_ADDRESS_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + { + return total_saved_regs_space + total_auto_space; + } +} + +fualign (direction, x, y) +{ + + return (direction > 0 ? ((x + (y - 1)) & -y) : (x & (y - 1))); + +} + +faa (CUM, MODE, TYPE, NAMED) + int CUM; + enum machine_mode MODE; + tree TYPE; + int NAMED; +{ + int nrs; + int align; + + switch (MODE) + { + case QImode: + case HImode: + nrs = 1; + break; + + case SFmode: + case PSImode: + case SImode: + nrs = 2; + break; + case DFmode: + case DImode: + nrs = 4; + break; + case VOIDmode: + case BLKmode: + default: + nrs = (int_size_in_bytes (TYPE) + 1) / 2; + break; + } + + CUM = CUM - (nrs - 1); + while (CUM >= 2 + && !HARD_REGNO_MODE_OK (CUM, MODE)) + { + CUM--; + } + + return CUM - 1; + +} + +/* Stuff taken from m88k.c */ + +/* Output to FILE the start of the assembler file. */ + +struct options +{ + char *string; + int *variable; + int on_value; + char *description; +}; + +static int +output_option (file, sep, type, name, indent, pos, max) + FILE *file; + char *sep; + char *type; + char *name; + char *indent; + int pos; + int max; +{ + if (strlen (sep) + strlen (type) + strlen (name) + pos > max) + { + fprintf (file, indent); + return fprintf (file, "%s%s", type, name); + } + return pos + fprintf (file, "%s%s%s", sep, type, name); +} + +static struct +{ + char *name; + int value; +} + +m_options[] = TARGET_SWITCHES; + +static void +output_options (file, f_options, f_len, W_options, W_len, + pos, max, sep, indent, term) + FILE *file; + struct options *f_options; + struct options *W_options; + int f_len, W_len; + int pos; + int max; + char *sep; + char *indent; + char *term; +{ + register int j; + extern int flag_traditional; + + if (optimize) + pos = output_option (file, sep, "-O", "", indent, pos, max); + if (write_symbols != NO_DEBUG) + pos = output_option (file, sep, "-g", "", indent, pos, max); + if (flag_traditional) + pos = output_option (file, sep, "-traditional", "", indent, pos, max); + if (profile_flag) + pos = output_option (file, sep, "-p", "", indent, pos, max); + if (profile_block_flag) + pos = output_option (file, sep, "-a", "", indent, pos, max); + + for (j = 0; j < f_len; j++) + if (*f_options[j].variable == f_options[j].on_value) + pos = output_option (file, sep, "-f", f_options[j].string, + indent, pos, max); + + for (j = 0; j < W_len; j++) + if (*W_options[j].variable == W_options[j].on_value) + pos = output_option (file, sep, "-W", W_options[j].string, + indent, pos, max); + + for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) + if (m_options[j].name[0] != '\0' + && m_options[j].value > 0 + && ((m_options[j].value & target_flags) + == m_options[j].value)) + pos = output_option (file, sep, "-m", m_options[j].name, + indent, pos, max); + + + +} + + +asm_file_start (file, f_options, f_len, W_options, W_len) + FILE *file; + struct options *f_options; + struct options *W_options; + int f_len, W_len; +{ + int i; + + extern int arg_regs_order[]; + extern char *main_input_filename; + extern int arg_nregs; + extern char call_used_regs[]; + extern char *input_filename; + + char *quote = TARGET_YASM ? "" : "\""; + + fprintf (file, "!\tGCC Z8000\n"); + fprintf (file, "!\tCygnus Support\n"); + fprintf (file, "!\tsizeof(size_t)=%d\n", TARGET_BIG ? 4 : 2); + + output_file_directive (file, main_input_filename); + fprintf (file, "!"); + output_options (file, f_options, f_len, W_options, W_len, + 0, 75, " ", "\n! ", "\n\n"); + + fprintf (file, "\n\n"); + + if (TARGET_BIG) + fprintf (file, "\tsegm\n"); + else + fprintf (file, "\tunseg\n"); + + if (TARGET_YASM) + { + fprintf (file, "\trsect USRROM\n"); + fprintf (file, "\teven\n"); + fprintf (file, "\trsect USRRAM\n"); + fprintf (file, "\teven\n"); + fprintf (file, "\trsect USRTXT\n"); + fprintf (file, "\teven\n"); + } + + load_source_file (input_filename); +} + +void +asm_file_end (file) + FILE *file; +{ + if (TARGET_YASM) + { + struct extern_list *p; + + for (p = extern_head; p != 0; p = p->next) + { + tree name_tree = get_identifier (p->name); + + if (!TREE_ASM_WRITTEN (name_tree)) + { + TREE_ASM_WRITTEN (name_tree) = 1; + fputs ("\textern\t", file); + assemble_name (file, p->name); + fputs ("\n", file); + } + } + + fprintf (file, "\tend\n"); + } +} + + +/********************************************************************************************* + * Source Program Listing Generation Management Routines * + *********************************************************************************************/ + +#ifndef LINE_LEN +#define LINE_LEN 1000 +#endif + + +/* 'line_no' - contains actual source line number starting from 1. + 'line' - points to the actual source line. */ +struct source_line_node +{ + struct source_line_node *prev, *next; + int line_no; + char *line; +}; +typedef struct source_line_node line_node; + +/* 'name' - points to the file name. + 'head' - points to the first source line. + 'current_line' - points to the last referenced source line. */ +struct source_file_node + { + struct source_file_node *next; + char *name; + line_node *head, *current_line; + }; +typedef struct source_file_node file_node; + +static file_node *file_head = 0, *current_file = 0; + +void +add_line (line_no, source_line) + int line_no; + char source_line[]; +{ + if (file_head) + { + line_node *temp_node; + + temp_node = (line_node *) (malloc (sizeof (line_node))); + temp_node->line_no = line_no; + temp_node->line = (char *) (malloc (strlen (source_line) + 1)); + strcpy (temp_node->line, source_line); + + if (current_file->head) + { + temp_node->prev = current_file->current_line; + temp_node->next = 0; + current_file->current_line->next = temp_node; + current_file->current_line = temp_node; + } + else + { + temp_node->prev = temp_node->next = 0; + current_file->head = current_file->current_line = temp_node; + } + } +} + +void +load_single_file (file_name) + char *file_name; +{ + FILE *source_fp; + char source_line[LINE_LEN]; + int ch; + int line_no = 1; + int col_no = 0; + + if ((source_fp = fopen (file_name, "r")) == 0) + { + char global_file_name[LINE_LEN]; + sprintf (global_file_name, "%s%s", "/usr/include", file_name); + source_fp = fopen (global_file_name, "r"); + /* If file is pre-processed with cpp not all files can be opened. */ + } + + /* If not found leave current_file->head alone. This is an indication + that the source file is not found. */ + if (source_fp != 0) + { + while ((ch = fgetc (source_fp)) != EOF) + switch (ch) + { + case '\n': + /* End-of-line character (\n) is added in print_source_line. */ + source_line[col_no] = '\0'; + add_line (line_no, source_line); + col_no = 0; + line_no++; + break; + default: + if (col_no < LINE_LEN - 1) + source_line[col_no++] = ch; + break; + } + fclose (source_fp); + } +} + + +load_source_file (file_name) + char *file_name; +{ + file_node *temp_node, *tail_node = file_head; + + for (temp_node = file_head; temp_node != 0; tail_node = temp_node, temp_node = temp_node->next) + if (strcmp (temp_node->name, file_name) == 0) + { + current_file = temp_node; + break; + } + + if (temp_node == 0) + { + temp_node = (file_node *) (malloc (sizeof (file_node))); + temp_node->next = 0; + temp_node->name = (char *) (malloc (strlen (file_name) + 1)); + strcpy (temp_node->name, file_name); + temp_node->head = temp_node->current_line = 0; + + if (file_head) + tail_node->next = temp_node; + else + file_head = temp_node; + + current_file = temp_node; + load_single_file (file_name); + } +} + +void +print_source_line (file, line_no) + FILE *file; + int line_no; +{ + if (TARGET_SOURCE && current_file) + { + + /* If current_file->head is null it definitely implies that no source file + has been found in the working directory. However, if current_line is null + while head is not null, it means that no source line has been printed. */ + if (!current_file->head) + fprintf (file, "! Line number:%d\n", line_no); + else if (!current_file->current_line) + { + for (current_file->current_line = current_file->head; + current_file->current_line + && current_file->current_line->line_no != line_no; + current_file->current_line = current_file->current_line->next) + ; + + if (current_file->current_line && current_file->current_line->line) + fprintf (file, "! %s\n", current_file->current_line->line); + } + else if (current_file->current_line->line_no < line_no) + { + for (current_file->current_line = current_file->current_line->next; + current_file->current_line + && current_file->current_line->line_no <= line_no; + current_file->current_line = current_file->current_line->next) + { + fprintf (file, "! %s\n", current_file->current_line->line); + if (current_file->current_line->line_no == line_no) + break; + } + } + else + { + for (; current_file->current_line->line_no != line_no; + current_file->current_line = current_file->current_line->prev) + ; + if (current_file->current_line) + fprintf (file, "! %s\n", current_file->current_line->line); + } + } + + if (TARGET_LINE) + { + fprintf (file, ".line %d\n", line_no); + } +} + + + +/**********************************************************************/ + +/* Code to generate prologue and epilogue sequences */ + + + +rtx +gen_push (operand0, mode) + rtx operand0; + enum machine_mode mode; +{ + return gen_rtx (SET, VOIDmode, + gen_rtx (MEM, mode, gen_rtx (PRE_DEC, mode, + gen_rtx (REG, pmode, STACK_POINTER_REGNUM))), operand0); +} + +rtx +gen_pop (operand0, mode) + rtx operand0; + enum machine_mode mode; +{ + return gen_rtx (SET, VOIDmode, + operand0, + gen_rtx (MEM, mode, gen_rtx (POST_INC, mode, + gen_rtx (REG, pmode, STACK_POINTER_REGNUM)))); +} + +void +push (rn, mode) + int rn; + enum machine_mode mode; +{ + emit_insn (gen_push (gen_rtx (REG, mode, rn), mode)); +} + +void +pop (rn, mode) + enum machine_mode mode; +{ + emit_insn (gen_pop (gen_rtx (REG, mode, rn), mode)); +} + + +/* Generate code to push the regs specified in the mask. remember + that the mask is of the internal shape of the regs, not the + external shape - so go through the renumber vector */ + +static void +push_regs (mask) + int mask; +{ + int i; + + int size = 0; + /* The mask is a bit-for-bit for the internal register numbering system, + so r10 has the fp in it. We work out the external register numbering + so that we can push the registers efficiently */ + int exregs[FIRST_PSEUDO_REGISTER]; + + memset (exregs, 0, sizeof (exregs)); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (mask & (1<<i)) + { + exregs[renumber[i]] = 1; + } + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i+=2) + { + if (exregs[i] && exregs[i+1]) + { + push (rev_renumber[i], SImode); + } + else if (exregs[i]) + { + push (rev_renumber[i], HImode); + } + else if (exregs[i+1]) + { + push (rev_renumber[i+1], HImode); + } + } + +} + + +static void +pop_regs (mask) + int mask; +{ + int i; + int j; + + int size = 0; + /* The mask is a bit-for-bit for the internal register numbering system, + so r10 has the fp in it. We work out the external register numbering + so that we can push the registers efficiently */ + int exregs[16]; + + memset (exregs, 0, sizeof (exregs)); + + for (i = 0; i < 16; i++) + { + if (mask & (1<<i)) + { + exregs[renumber[i]] = 1; + } + } + + for (j = 0; j < 16; j+=2) + { + i = 14 - j; + if (exregs[i] && exregs[i+1]) + { + pop (rev_renumber[i], SImode); + } + else if (exregs[i]) + { + pop (rev_renumber[i], HImode); + } + else if (exregs[i+1]) + { + pop (rev_renumber[i+1], HImode); + } + } + +} + +/* Adjust the stack and return the number of bytes taken to do it */ + +static void +output_stack_adjust (direction, size) + int direction; + int size; +{ + if (size) + { + rtx val = GEN_INT (size * direction); + rtx insn; + + insn = (TARGET_BIG ? gen_addpsi3 : gen_addhi3) (stack_pointer_rtx, stack_pointer_rtx, val); + + emit_insn (insn); + } +} + +void +z8k_expand_prologue () +{ + int live_regs_mask; + int d; + + live_regs_mask = calc_live_regs (&d); + + output_stack_adjust (-1, current_function_pretend_args_size); + + if (frame_pointer_needed) + { + push_regs (live_regs_mask); + if (TARGET_BIG) + emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); + else + emit_insn (gen_movhi (frame_pointer_rtx, stack_pointer_rtx)); + } + else + { + push_regs (live_regs_mask); + } + + output_stack_adjust (-1, get_frame_size ()); +} + +void +z8k_declare_function_name (file, name, decl) + FILE *file; + char *name; + tree decl; +{ + int reg_count; + int parm_size = 0; + tree parms; + + /* Figure out how many registers are saved. */ + calc_live_regs (®_count); + + /* Find parameters which are passed in registers, but live on + this function's stack. */ + parms = DECL_ARGUMENTS (decl); + for (; parms; parms = TREE_CHAIN (parms)) + { + if (GET_CODE (DECL_RTL (parms)) == MEM + && XEXP (DECL_RTL (parms), 0) != const0_rtx + && ! CONSTANT_P (XEXP (DECL_RTL (parms), 0))) + parm_size += GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))); + } + + /* Output the information. */ + fprintf (file, "! stack frame requirements for %s: %d bytes\n", name, + (reg_count * 2 + get_frame_size () + + current_function_pretend_args_size)); + fprintf (file, "! register saves: %d bytes\n", reg_count * 2); + fprintf (file, "! automatics, spills, etc: %d bytes\n", + get_frame_size () - parm_size); + fprintf (file, "! parameters: %d bytes\n", parm_size); + fprintf (file, "! varargs flushback area: %d bytes\n", + current_function_pretend_args_size); + + /* Now output the label for this function. */ + asm_output_name (file, name); + +} + +void +z8k_expand_epilogue () +{ + int live_regs_mask; + int d; + int i; + int need; + live_regs_mask = calc_live_regs (&d); + + if (frame_pointer_needed) + { + if (TARGET_BIG) + emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); + else + emit_insn (gen_movhi (stack_pointer_rtx, frame_pointer_rtx)); + need = 0; + } + else + { + need = get_frame_size (); + } + + if (live_regs_mask) + { + if (need) + { + output_stack_adjust (1, need); + need = 0; + } + + pop_regs (live_regs_mask); + } + output_stack_adjust (1, extra_push + need + + current_function_pretend_args_size); + + current_function_anonymous_args = 0; +} + +int +pop_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + return 0; + + if (GET_MODE (op) != mode) + return 0; + + op = XEXP (op, 0); + + if (GET_CODE (op) != POST_INC) + return 0; + + return XEXP (op, 0) == stack_pointer_rtx; +} + +int +smallint_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT + && (CONST_OK_FOR_LETTER_P (INTVAL (op), 'J') + || CONST_OK_FOR_LETTER_P (INTVAL (op), 'K')); +} + + +int +address_cost (mode) + rtx mode; +{ + if (GET_CODE (mode) == PLUS) + { + rtx lhs = XEXP (mode, 0); + rtx rhs = XEXP (mode, 1); + if (GET_CODE (lhs) == REG + && GET_CODE (rhs) == CONST_INT) + return 10; + + if (GET_CODE (lhs) == MEM) + return 40; + } + return 1; +} + +int +power_two_operand (operand, mode) + rtx operand; + enum machine_mode mode; +{ + if (GET_CODE (operand) == CONST_INT) + { + int i; + return POWER_OF_2 (INTVAL (operand)); + } + return 0; +} + +COM_POWER_OF_2 (value) + int value; +{ + return POWER_OF_2 (~(value | (~0xffff))); +} + +com_power_two_operand (operand, mode) + rtx operand; + enum machine_mode mode; +{ + + if (GET_CODE (operand) == CONST_INT) + return COM_POWER_OF_2 (INTVAL (operand)); + return 0; +} + + +int +prd_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + return 0; + if (GET_MODE (op) != mode) + return 0; + op = XEXP (op, 0); + if (GET_CODE (op) != PRE_DEC) + return 0; + return register_operand (XEXP (op, 0), Pmode); +} + +int +poi_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + return 0; + if (GET_MODE (op) != mode) + return 0; + op = XEXP (op, 0); + if (GET_CODE (op) != POST_INC) + return 0; + return register_operand (XEXP (op, 0), Pmode); +} + + +r_im_ir_da_x_ba_bx_poi_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || im_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode) + || poi_operand (op, mode); + return r; +} + +int +r_ir_da_x_ba_bx_poi_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode) + || poi_operand (op, mode); + return r; +} + +int +r_ir_da_x_ba_bx_prd_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int r; + + r = r_operand (op, mode) + || ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode) + || prd_operand (op, mode); + return r; +} + + +rtx +legitimize_address (oldx, mode) + rtx oldx; + enum machine_mode mode; +{ +#if 0 + if (TARGET_PIC) + { + if (GET_CODE (oldx) == SYMBOL_REF + || GET_CODE (oldx) == LABEL_REF) + { + rtx ptr = gen_reg_rtx (Pmode); + emit_insn (gen_rtx (SET, VOIDmode, ptr, + gen_rtx (LO_SUM, pic_reg, oldx))); + return gen_rtx (MEM, mode, ptr); + } + } +#endif + return oldx; +} + +symbol_ref (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == SYMBOL_REF) + return 1; + return 0; +} + +not_subreg_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == REG); +} + +BADSUBREG (op) + rtx op; +{ + /* Can't subreg someting like subreg:HI (mem:SI (plus: reg reg) ) + cause there's no room to put the extra +1 to get to the low part */ + + + if (GET_CODE (op) == SUBREG) + { + /* Always ok if want the high word */ + rtx inside; + + /* Subreg of a reg is ok too */ + if (GET_CODE (SUBREG_REG (op)) == REG) + return 0; + + inside = XEXP (op, 0); + if (GET_CODE (inside) == MEM) + { + + /* Can't do paradoxical subregs in memory */ + if (GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (inside))) + return 1; + /* Ok if want the low part of a mem(symbol_ref), mem(reg), or mem(reg+k) + + since they can have indexing added to them */ + inside = XEXP (inside, 0); + + if (inside_da_p (inside, 0) + || inside_x_p (inside, 0) + || inside_ba_p (inside, 0)) + return 0; + return 1; + /* If you want the high part (the low addressed bit) then the other modes + work too */ + if (SUBREG_WORD (op) == 0 && 0) + { + + if (inside_bx_p (inside, 0)) + return 0; + + } + + + + return 1; + } + } + return 0; +} + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ + +enum reg_class +secondary_reload_class (class, mode, in) + enum reg_class class; + enum machine_mode mode; + rtx in; +{ + int regno = -1; + enum rtx_code code = GET_CODE (in); + + /* Can move all but qis any time */ + if (mode != QImode) + return NO_REGS; + + /* Can easily move in and out of qi regs */ + if (class == QI_REGS) + return NO_REGS; + if (!CONSTANT_P (in)) + { + regno = true_regnum (in); + + /* A pseudo is the same as memory. */ + if (regno == -1 || regno >= FIRST_PSEUDO_REGISTER) + code = MEM; + } + + /* If between memory we may need QI */ + + if (code == MEM) + return SQI_REGS; + + return NO_REGS; + +} + +int +ir_da_x_ba_bx_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + + return ir_operand (op, mode) + || da_operand (op, mode) + || x_operand (op, mode) + || ba_operand (op, mode) + || bx_operand (op, mode); +} + +int +r_da_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return r_operand (op, mode) || da_operand (op, mode); +} + + +int +immediate15_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT && (!(INTVAL (op) & 0x8000)); +} + +int +moveok (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + if (r_operand (operands[0], mode) && r_ir_da_x_ba_bx_operand (operands[1], mode)) + return 1; + if (ir_da_x_ba_bx_operand (operands[0], mode) && r_operand (operands[1], mode)) + return 1; + if (push_operand (operands[0], mode) && r_im_ir_da_x_operand (operands[1], mode)) + return 1; + if (r_ir_da_x_operand (operands[0], mode) && pop_operand (operands[1], mode)) + return 1; + if (mode == HImode || mode == QImode) + if (r_ir_da_x_operand (operands[0], mode) && immediate_operand (operands[1], mode)) + return 1; + if (mode == SImode || mode == SFmode || mode == PSImode) + if (r_da_operand (operands[0], mode) && immediate_operand (operands[1], mode)) + return 1; + return 0; +} + + +void +asm_output_ascii (file, p, size) + FILE *file; + char *p; + int size; +{ + int i, col; + if (TARGET_YASM) + { + int max_buffer_size = 70; + fprintf (file, "\tsval\t'"); + for (i = col = 0; i < size; i++, col++) + { + register unsigned int c = (p[i] & 0xff); + if (c != ';' && c != '\\' && c != '\'' && c != '\"' && c != '%' && c >= ' ' && c < 0177) + putc (c, file); + else + fprintf (file, "%%%02x", c); + /* If line is too long split the line */ + if (col > max_buffer_size && i < size - 1) + { + fprintf (file, "'\n\tsval\t'"); + col = 0; + } + } + fprintf (file, "\047\n"); + } + else + { + FILE *_hide_asm_out_file = file; + unsigned char *_hide_p = (unsigned char *) p; + int _hide_thissize = size; + { + FILE *asm_out_file = _hide_asm_out_file; + unsigned char *p = _hide_p; + int thissize = _hide_thissize; + int i; + fprintf (asm_out_file, "\t.ascii \""); + + for (i = 0; i < thissize; i++) + { + register int c = p[i]; + if (c == '\"' || c == '\\') + putc ('\\', asm_out_file); + if (c >= ' ' && c < 0177) + putc (c, asm_out_file); + else + { + fprintf (asm_out_file, "\\%o", c); + /* After an octal-escape, if a digit follows, + terminate one string constant and start another. + The Vax assembler fails to stop reading the escape + after three digits, so this is the only way we + can get it to parse the data properly. */ + if (i < thissize - 1 + && p[i + 1] >= '0' && p[i + 1] <= '9') + fprintf (asm_out_file, "\"\n\t.ascii \""); + } + } + fprintf (asm_out_file, "\"\n"); + } + } +} + +/* Keep track of all externs, so that we can output an .extern declaration + for them at the end of the file, but only if they are not defined in + this file. We assume that all names passed to us are in the permanent + obstack, so that they will be valid at the end of the compilation. */ + +void +z8k_output_external (name) + char *name; +{ + struct extern_list *p; + + p = (struct extern_list *) permalloc ((long) sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + extern_head = p; +} + +/* Work out which way a 64bit move should happen - + least or most significant word first */ +static +int +whichway (dst, src) + rtx dst; + rtx src; +{ + if (GET_CODE (dst) == REG) + { + int rdst = REGNO (dst); + rtx dst_msw = gen_rtx (REG, SImode, rdst + 2); + if (GET_CODE (src) == REG) + { + int rsrc = REGNO (src); + if (rdst == rsrc + 2) + return -1; + else + return 1; + } + + /* if msw of dst is in the src, move the lsw first */ + if (reg_overlap_mentioned_p (dst_msw, src)) + { + return 1; + } + if (reg_overlap_mentioned_p (dst, src)) + { + return -1; + } + + + } + + return 1; +} + +/* Return string to move 64 bit operand without trampling arguments. */ + +char * +output_move64 (dst, src) + rtx dst; + rtx src; +{ + if (push_operand (dst, GET_MODE (dst))) + { + /* Easy, unless source uses stack pointer */ + if (reg_overlap_mentioned_p (stack_pointer_rtx, src)) + { + return "pushl @%H0,%T1\n\tpushl @%H0,%T1"; + } + return "pushl @%H0,%T1\n\tpushl @%H0,%S1"; + } + if (whichway (dst, src) < 0) + return "$ldl %T0,%T1\n\t$ldl %S0,%S1 ! di a"; + else + return "$ldl %S0,%S1\n\t$ldl %T0,%T1 ! di b"; +} |