diff options
author | YamaArashi <shadow962@live.com> | 2016-01-06 01:47:28 -0800 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-01-06 01:47:28 -0800 |
commit | be8b04496302184c6e8f04d6179f9c3afc50aeb6 (patch) | |
tree | 726e2468c0c07add773c0dbd86ab6386844259ae /gcc/config/convex/convex.c |
initial commit
Diffstat (limited to 'gcc/config/convex/convex.c')
-rwxr-xr-x | gcc/config/convex/convex.c | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/gcc/config/convex/convex.c b/gcc/config/convex/convex.c new file mode 100755 index 0000000..cd2eb55 --- /dev/null +++ b/gcc/config/convex/convex.c @@ -0,0 +1,675 @@ +/* Subroutines for insn-output.c for Convex. + Copyright (C) 1988, 1993, 1994, 1997 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, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include <stdio.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" + +/* 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, + convert_to_mode (TYPE_MODE (sizetype), operands[2], + TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +} + +#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. */ +} + |