home *** CD-ROM | disk | FTP | other *** search
- /* Subroutines for insn-output.c for Convex.
- Copyright (C) 1988, 1993, 1994 Free Software Foundation, Inc.
-
- 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 1, 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 "config.h"
- #include "tree.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 "insn-attr.h"
- #include "output.h"
- #include "expr.h"
-
- #undef NULL
- #include <stdio.h>
-
- /* Tables used in convex.h */
-
- char regno_ok_for_index_p_base[1 + LAST_VIRTUAL_REGISTER + 1];
- enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
- enum reg_class reg_class_from_letter[256];
-
- /* Target cpu index. */
-
- int target_cpu;
-
- /* Boolean to keep track of whether the current section is .text or not.
- Used by .align handler in convex.h. */
-
- int current_section_is_text;
-
- /* Communication between output_compare and output_condjump. */
-
- static rtx cmp_operand0, cmp_operand1;
- static char cmp_modech;
-
- /* Forwards */
-
- static rtx frame_argblock;
- static int frame_argblock_size;
- static rtx convert_arg_pushes ();
- static void expand_movstr_call ();
-
- /* Here from OVERRIDE_OPTIONS at startup. Initialize constant tables. */
-
- init_convex ()
- {
- int regno;
-
- /* Set A and S reg classes. */
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (A_REGNO_P (regno))
- {
- regno_ok_for_index_p[regno] = 1;
- regno_reg_class[regno] = INDEX_REGS;
- }
- else
- {
- regno_ok_for_index_p[regno] = 0;
- regno_reg_class[regno] = S_REGS;
- }
-
- /* Can't index off the stack pointer, register 0. */
- regno_ok_for_index_p[STACK_POINTER_REGNUM] = 0;
- regno_reg_class[STACK_POINTER_REGNUM] = SP_REGS;
-
- /* Can't index off aliases of the stack pointer. */
- regno_ok_for_index_p[VIRTUAL_INCOMING_ARGS_REGNUM] = 1;
- regno_ok_for_index_p[VIRTUAL_STACK_VARS_REGNUM] = 1;
- regno_ok_for_index_p[VIRTUAL_STACK_DYNAMIC_REGNUM] = 0;
- regno_ok_for_index_p[VIRTUAL_OUTGOING_ARGS_REGNUM] = 0;
-
- /* Can't index off hard reg -1 == pseudos not assigned */
- regno_ok_for_index_p[-1] = 0;
-
- /* Set reg class letters */
- reg_class_from_letter['a'] = A_REGS;
- reg_class_from_letter['A'] = INDEX_REGS;
- reg_class_from_letter['d'] = S_REGS;
-
- /* Turn off floating point exception enables in the psw. */
- psw_disable_float ();
- }
-
- psw_disable_float ()
- {
- #if __convex__ && __GNUC__
- register int *p;
- asm ("mov fp,%0" : "=a" (p));
- while (p)
- {
- p[1] &= ~0x1000c400;
- p = (int *) p[2];
- }
- #endif
- }
-
- /* Here to output code for a compare insn. Output nothing, just
- record the operands and their mode. */
-
- char *
- output_cmp (operand0, operand1, modech)
- rtx operand0, operand1;
- char modech;
- {
- cmp_operand0 = operand0;
- cmp_operand1 = operand1;
- cmp_modech = modech;
- return "";
- }
-
- /* Output code for a conditional jump. The preceding instruction
- is necessarily a compare. Output two instructions, for example
- eq.w a1,a2
- jbra.t L5
- for
- (cmpsi a1 a2)
- (beq L5)
- */
-
- char *
- output_condjump (label, cond, jbr_sense)
- rtx label;
- char *cond;
- char jbr_sense;
- {
- rtx operands[3];
- char cmp_op[4];
- char buf[80];
- char jbr_regch;
-
- strcpy (cmp_op, cond);
-
- /* [BL] mean the value is being compared against immediate 0.
- Use neg.x, which produces the same carry that eq.x #0 would if it
- existed. In this case operands[1] is a scratch register, not a
- compare operand. */
-
- if (cmp_modech == 'B' || cmp_modech == 'L')
- {
- cmp_modech = cmp_modech - 'A' + 'a';
- strcpy (cmp_op, "neg");
- }
-
- /* [WH] mean the value being compared resulted from "add.[wh] #-1,rk"
- when rk was nonnegative -- we can omit equality compares against -1
- or inequality compares against 0. */
-
- else if (cmp_modech == 'W' || cmp_modech == 'H')
- {
- if (! strcmp (cmp_op, "eq") && cmp_operand1 == constm1_rtx)
- jbr_sense ^= 't' ^ 'f';
- else if (! strcmp (cmp_op, "lt") && cmp_operand1 == const0_rtx)
- ;
- else
- cmp_modech = cmp_modech - 'A' + 'a';
- }
-
- /* Constant must be first; swap operands if necessary.
- If lt, le, ltu, leu are swapped, change to le, lt, leu, ltu
- and reverse the sense of the jump. */
-
- if (! REG_P (cmp_operand1))
- {
- operands[0] = cmp_operand1;
- operands[1] = cmp_operand0;
- if (cmp_op[0] == 'l')
- {
- cmp_op[1] ^= 'e' ^ 't';
- jbr_sense ^= 't' ^ 'f';
- }
- }
- else
- {
- operands[0] = cmp_operand0;
- operands[1] = cmp_operand1;
- }
-
- operands[2] = label;
-
- if (S_REG_P (operands[1]))
- jbr_regch = 's';
- else if (A_REG_P (operands[1]))
- jbr_regch = 'a';
- else
- abort ();
-
- if (cmp_modech == 'W' || cmp_modech == 'H')
- sprintf (buf, "jbr%c.%c %%l2", jbr_regch, jbr_sense);
- else
- sprintf (buf, "%s.%c %%0,%%1\n\tjbr%c.%c %%l2",
- cmp_op, cmp_modech, jbr_regch, jbr_sense);
- output_asm_insn (buf, operands);
- return "";
- }
-
- /* Return 1 if OP is valid for cmpsf.
- In IEEE mode, +/- zero compares are not handled by
- the immediate versions of eq.s and on some machines, lt.s, and le.s.
- So disallow 0.0 as the immediate operand of xx.s compares in IEEE mode. */
-
- int
- nonmemory_cmpsf_operand (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- #if _IEEE_FLOAT_
- if (op == CONST0_RTX (SFmode))
- return 0;
- #endif
-
- return nonmemory_operand (op, mode);
- }
-
- /* Convex /bin/as does not like unary minus in some contexts.
- Simplify CONST addresses to remove it. */
-
- rtx
- simplify_for_convex (x)
- rtx x;
- {
- switch (GET_CODE (x))
- {
- case MINUS:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) < 0)
- {
- PUT_CODE (x, PLUS);
- XEXP (x, 1) = GEN_INT (- INTVAL (XEXP (x, 1)));
- }
- break;
-
- case CONST:
- return simplify_for_convex (XEXP (x, 0));
- }
-
- return x;
- }
-
- /* Routines to separate CONST_DOUBLEs into component parts. */
-
- int
- const_double_high_int (x)
- rtx x;
- {
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
- return CONST_DOUBLE_LOW (x);
- else
- return CONST_DOUBLE_HIGH (x);
- }
-
- int
- const_double_low_int (x)
- rtx x;
- {
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
- return CONST_DOUBLE_HIGH (x);
- else
- return CONST_DOUBLE_LOW (x);
- }
-
- /* Inline block copy. */
-
- void
- expand_movstr (operands)
- rtx *operands;
- {
- rtx dest = operands[0];
- rtx src = operands[1];
- int align = INTVAL (operands[3]);
- int nregs, maxsize;
- unsigned len;
- enum machine_mode mode;
- rtx reg, load, store, prev_store, prev_store_2;
- int size;
-
- /* Decide how many regs to use, depending on load latency, and what
- size pieces to move, depending on whether machine does unaligned
- loads and stores efficiently. */
-
- if (TARGET_C1)
- {
- /* ld.l latency is 4, no alignment problems. */
- nregs = 3, maxsize = 8;
- }
- else if (TARGET_C2)
- {
- /* loads are latency 2 if we avoid ld.l not at least word aligned. */
- if (align >= 4)
- nregs = 2, maxsize = 8;
- else
- nregs = 2, maxsize = 4;
- }
- else if (TARGET_C34)
- {
- /* latency is 4 if aligned, horrible if not. */
- nregs = 3, maxsize = align;
- }
- else if (TARGET_C38)
- {
- /* latency is 2 if at least word aligned, 3 or 4 if unaligned. */
- if (align >= 4)
- nregs = 2, maxsize = 8;
- else
- nregs = 3, maxsize = 8;
- }
- else
- abort ();
-
- /* Caller is not necessarily prepared for us to fail in this
- expansion. So fall back by generating memcpy call here. */
-
- if (GET_CODE (operands[2]) != CONST_INT
- || (len = INTVAL (operands[2])) > (unsigned) 32 * maxsize)
- {
- expand_movstr_call (operands);
- return;
- }
-
- reg = 0;
- prev_store = prev_store_2 = 0;
-
- while (len > 0)
- {
- if (len >= 8 && maxsize >= 8)
- mode = DImode;
- else if (len >= 4 && maxsize >= 4)
- mode = SImode;
- else if (len >= 2 && maxsize >= 2)
- mode = HImode;
- else
- mode = QImode;
-
- /* If no temp pseudo to reuse, or not the right mode, make one */
- if (! reg || GET_MODE (reg) != mode)
- reg = gen_reg_rtx (mode);
-
- /* Get src and dest in the right mode */
- if (GET_MODE (src) != mode)
- src = change_address (src, mode, 0),
- dest = change_address (dest, mode, 0);
-
- /* Make load and store patterns for this piece */
- load = gen_rtx (SET, VOIDmode, reg, src);
- store = gen_rtx (SET, VOIDmode, dest, reg);
-
- /* Emit the load and the store from last time.
- When we emit a store, we can reuse its temp reg. */
- emit_insn (load);
- if (prev_store)
- {
- reg = SET_SRC (prev_store);
- emit_insn (prev_store);
- }
- else
- reg = 0;
-
- /* Queue up the store, for next time or the time after that. */
- if (nregs == 2)
- prev_store = store;
- else
- prev_store = prev_store_2, prev_store_2 = store;
-
- /* Advance to next piece. */
- size = GET_MODE_SIZE (mode);
- src = adj_offsettable_operand (src, size);
- dest = adj_offsettable_operand (dest, size);
- len -= size;
- }
-
- /* Finally, emit the last stores. */
- if (prev_store)
- emit_insn (prev_store);
- if (prev_store_2)
- emit_insn (prev_store_2);
- }
-
- static void
- expand_movstr_call (operands)
- rtx *operands;
- {
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
- VOIDmode, 3,
- XEXP (operands[0], 0), Pmode,
- XEXP (operands[1], 0), Pmode,
- operands[2], SImode);
- }
-
- #if _IEEE_FLOAT_
- #define MAX_FLOAT 3.4028234663852886e+38
- #define MIN_FLOAT 1.1754943508222875e-38
- #else
- #define MAX_FLOAT 1.7014117331926443e+38
- #define MIN_FLOAT 2.9387358770557188e-39
- #endif
-
- int
- check_float_value (mode, dp, overflow)
- enum machine_mode mode;
- REAL_VALUE_TYPE *dp;
- int overflow;
- {
- REAL_VALUE_TYPE d = *dp;
-
- if (overflow)
- {
- *dp = MAX_FLOAT;
- return 1;
- }
-
- if (mode == SFmode)
- {
- if (d > MAX_FLOAT)
- {
- *dp = MAX_FLOAT;
- return 1;
- }
- else if (d < -MAX_FLOAT)
- {
- *dp = -MAX_FLOAT;
- return 1;
- }
- else if ((d > 0 && d < MIN_FLOAT) || (d < 0 && d > -MIN_FLOAT))
- {
- *dp = 0.0;
- return 1;
- }
- }
-
- return 0;
- }
-
- /* Output the label at the start of a function.
- Precede it with the number of formal args so debuggers will have
- some idea of how many args to print. */
-
- void
- asm_declare_function_name (file, name, decl)
- FILE *file;
- char *name;
- tree decl;
- {
- tree parms;
- int nargs = list_length (DECL_ARGUMENTS (decl));
-
- char *p, c;
- extern char *version_string;
- static char vers[4];
- int i;
-
- p = version_string;
- for (i = 0; i < 3; ) {
- c = *p;
- if (c - '0' < (unsigned) 10)
- vers[i++] = c;
- if (c == 0 || c == ' ')
- vers[i++] = '0';
- else
- p++;
- }
- fprintf (file, "\tds.b \"g%s\"\n", vers);
-
- if (nargs < 100)
- fprintf (file, "\tds.b \"+%02d\\0\"\n", nargs);
- else
- fprintf (file, "\tds.b \"+00\\0\"\n");
-
- ASM_OUTPUT_LABEL (file, name);
- }
-
- /* Print an instruction operand X on file FILE.
- CODE is the code from the %-spec that requested printing this operand;
- if `%z3' was used to print operand 3, then CODE is 'z'. */
- /* Convex codes:
- %u prints a CONST_DOUBLE's high word
- %v prints a CONST_DOUBLE's low word
- %z prints a CONST_INT shift count as a multiply operand -- viz. 1 << n.
- */
-
- print_operand (file, x, code)
- FILE *file;
- rtx x;
- char code;
- {
- long u[2];
- REAL_VALUE_TYPE d;
-
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%s", reg_names[REGNO (x)]);
- break;
-
- case MEM:
- output_address (XEXP (x, 0));
- break;
-
- case CONST_DOUBLE:
- REAL_VALUE_FROM_CONST_DOUBLE (d, x);
- switch (GET_MODE (x)) {
- case DFmode:
- #if 0 /* doesn't work, produces dfloats */
- REAL_VALUE_TO_TARGET_DOUBLE (d, u);
- #else
- {
- union { double d; int i[2]; } t;
- t.d = d;
- u[0] = t.i[0];
- u[1] = t.i[1];
- }
- #endif
- if (code == 'u')
- fprintf (file, "#%#x", u[0]);
- else if (code == 'v')
- fprintf (file, "#%#x", u[1]);
- else
- outfloat (file, d, "%.17e", "#", "");
- break;
- case SFmode:
- outfloat (file, d, "%.9e", "#", "");
- break;
- default:
- if (code == 'u')
- fprintf (file, "#%d", CONST_DOUBLE_HIGH (x));
- else
- fprintf (file, "#%d", CONST_DOUBLE_LOW (x));
- }
- break;
-
- default:
- if (code == 'z')
- {
- if (GET_CODE (x) != CONST_INT)
- abort ();
- fprintf (file, "#%d", 1 << INTVAL (x));
- }
- else
- {
- putc ('#', file);
- output_addr_const (file, x);
- }
- }
- }
-
- /* Print a memory operand whose address is X, on file FILE. */
-
- print_operand_address (file, addr)
- FILE *file;
- rtx addr;
- {
- rtx index = 0;
- rtx offset = 0;
-
- if (GET_CODE (addr) == MEM)
- {
- fprintf (file, "@");
- addr = XEXP (addr, 0);
- }
-
- switch (GET_CODE (addr))
- {
- case REG:
- index = addr;
- break;
-
- case PLUS:
- index = XEXP (addr, 0);
- if (REG_P (index))
- offset = XEXP (addr, 1);
- else
- {
- offset = XEXP (addr, 0);
- index = XEXP (addr, 1);
- if (! REG_P (index))
- abort ();
- }
- break;
-
- default:
- offset = addr;
- break;
- }
-
- if (offset)
- output_addr_const (file, offset);
-
- if (index)
- fprintf (file, "(%s)", reg_names[REGNO (index)]);
- }
-
- /* Output a float to FILE, value VALUE, format FMT, preceded by PFX
- and followed by SFX. */
-
- outfloat (file, value, fmt, pfx, sfx)
- FILE *file;
- REAL_VALUE_TYPE value;
- char *fmt, *pfx, *sfx;
- {
- char buf[64];
- fputs (pfx, file);
- REAL_VALUE_TO_DECIMAL (value, fmt, buf);
- fputs (buf, file);
- fputs (sfx, file);
- }
-
- /* Here during RTL generation of return. If we are at the final return
- in a function, go through the function and replace pushes with stores
- into a frame arg block. This is similar to what ACCUMULATE_OUTGOING_ARGS
- does, but we must index off the frame pointer, not the stack pointer,
- and the calling sequence does not require the arg block to be at the
- top of the stack. */
-
- replace_arg_pushes ()
- {
- /* Doesn't work yet. */
- }
-
- /* Output the insns needed to do a call. operands[] are
- 0 - MEM, the place to call
- 1 - CONST_INT, the number of bytes in the arg list
- 2 - CONST_INT, the number of arguments
- 3 - CONST_INT, the number of bytes to pop
- 4 - address of the arg list.
- */
-
- char *
- output_call (insn, operands)
- rtx insn, *operands;
- {
- if (operands[4] == stack_pointer_rtx)
- output_asm_insn ("mov sp,ap", operands);
- else
- abort ();
-
- if (TARGET_ARGCOUNT)
- output_asm_insn ("pshea %a2", operands);
-
- output_asm_insn ("calls %0", operands);
-
- output_asm_insn ("ld.w 12(fp),ap", operands);
-
- if (operands[4] == stack_pointer_rtx && operands[3] != const0_rtx)
- output_asm_insn ("add.w %3,sp", operands);
-
- return "";
- }
-
-
- /* Here after reloading, before the second scheduling pass. */
-
- emit_ap_optimizations ()
- {
- /* Removed for now. */
- }
-
-