diff options
author | Chris Smith <cks@gnu.org> | 1993-05-07 11:54:24 +0000 |
---|---|---|
committer | Chris Smith <cks@gnu.org> | 1993-05-07 11:54:24 +0000 |
commit | 6cecf4ce3c74bc0b04b2de762de8b0123c1786b4 (patch) | |
tree | 3b74de5d3d2e10b0a3e552e4d529612ba9bae562 /gcc/config/convex/convex.c | |
parent | dba15dfd3c8f7767c2c27b4943d52f7c83b4b3b3 (diff) | |
download | gcc-6cecf4ce3c74bc0b04b2de762de8b0123c1786b4.zip gcc-6cecf4ce3c74bc0b04b2de762de8b0123c1786b4.tar.gz gcc-6cecf4ce3c74bc0b04b2de762de8b0123c1786b4.tar.bz2 |
*** empty log message ***
From-SVN: r4377
Diffstat (limited to 'gcc/config/convex/convex.c')
-rw-r--r-- | gcc/config/convex/convex.c | 1019 |
1 files changed, 907 insertions, 112 deletions
diff --git a/gcc/config/convex/convex.c b/gcc/config/convex/convex.c index e3a2bad..90d4308 100644 --- a/gcc/config/convex/convex.c +++ b/gcc/config/convex/convex.c @@ -1,5 +1,5 @@ /* Subroutines for insn-output.c for Convex. - Copyright (C) 1989,1991 Free Software Foundation, Inc. + Copyright (C) 1988,1993 Free Software Foundation, Inc. This file is part of GNU CC. @@ -18,6 +18,7 @@ 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" @@ -25,79 +26,235 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "insn-config.h" #include "conditions.h" #include "insn-flags.h" -#include "output.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; -/* set_cmp saves the operands of a "cmp" insn, along with the type character - * to be used in the compare instruction. - * - * gen_cmp finds out what comparison is to be performed and outputs the - * necessary instructions, e.g. - * "eq.w a1,a2\;jbra.t L5" - * for (cmpsi a1 a2) (beq L5) */ - -static rtx xop0, xop1; -static char typech, regch; +/* 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 * -set_cmp (op0, op1, typechr) - rtx op0, op1; - char typechr; -{ - xop0 = op0; - xop1 = op1; - typech = typechr; - if (GET_CODE (op0) == REG) - regch = A_REGNO_P (REGNO (op0)) ? 'a' : 's'; - else if (GET_CODE (op1) == REG) - regch = A_REGNO_P (REGNO (op1)) ? 'a' : 's'; - else abort (); +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 * -gen_cmp (label, cmpop, tf) +output_condjump (label, cond, jbr_sense) rtx label; - char *cmpop; - char tf; + char *cond; + char jbr_sense; { + rtx operands[3]; + char cmp_op[4]; char buf[80]; - char revop[4]; - rtx ops[3]; + 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. */ - ops[2] = label; + 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 (CONSTANT_P (xop1)) + if (! REG_P (cmp_operand1)) { - ops[0] = xop1; - ops[1] = xop0; - if (cmpop[0] == 'l') + operands[0] = cmp_operand1; + operands[1] = cmp_operand0; + if (cmp_op[0] == 'l') { - bcopy (cmpop, revop, sizeof revop); - revop[1] ^= 'e' ^ 't'; - tf ^= 't' ^ 'f'; - cmpop = revop; + cmp_op[1] ^= 'e' ^ 't'; + jbr_sense ^= 't' ^ 'f'; } } else { - ops[0] = xop0; - ops[1] = xop1; + operands[0] = cmp_operand0; + operands[1] = cmp_operand1; } - sprintf (buf, "%s.%c %%0,%%1\n\tjbr%c.%c %%l2", cmpop, typech, regch, tf); - output_asm_insn (buf, ops); + 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. */ @@ -121,100 +278,738 @@ const_double_low_int (x) return CONST_DOUBLE_LOW (x); } -/* Return the number of args in the call insn X. */ +/* Inline block copy. */ -static int -call_num_args (x) - rtx x; +void +expand_movstr (operands) + rtx *operands; { - if (GET_CODE (x) == CALL) - return INTVAL (x->fld[1].rtx); - if (GET_CODE (x) == SET) - return call_num_args (SET_SRC (x)); - abort (); + 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); } -/* Scan forward from a call to decide whether we need to reload AP - from 12(FP) after it. We need to if there can be a reference to - arg_pointer_rtx before the next call, which will clobber AP. - Look forward in the instruction list until encountering a call - (don't need the load), or a reference to AP (do need it), or - a jump (don't know, do the load). */ +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 -static int -ap_reload_needed (insn) - rtx insn; +void +check_float_value (mode, dp) + enum machine_mode mode; + REAL_VALUE_TYPE *dp; { - for (;;) + REAL_VALUE_TYPE d = *dp; + + if (mode == SFmode) { - insn = NEXT_INSN (insn); - switch (GET_CODE (insn)) + if (d > MAX_FLOAT) { - case JUMP_INSN: - /* Basic block ends. If return, no AP needed, else assume it is. */ - return GET_CODE (PATTERN (insn)) != RETURN; - case CALL_INSN: - /* A subsequent call. AP isn't needed unless the call itself - requires it. But zero-arg calls don't clobber AP, so - don't terminate the search in that case. */ - if (reg_mentioned_p (arg_pointer_rtx, PATTERN (insn))) - return 1; - if (! TARGET_ARGCOUNT && call_num_args (PATTERN (insn)) == 0) - break; - return 0; - case BARRIER: - /* Barrier, don't need AP. */ - return 0; - case INSN: - /* Other insn may need AP; if not, keep looking. */ - if (reg_mentioned_p (arg_pointer_rtx, PATTERN (insn))) - return 1; + error ("magnitude of constant too large for `float'"); + *dp = MAX_FLOAT; + } + else if (d < -MAX_FLOAT) + { + error ("magnitude of constant too large for `float'"); + *dp = -MAX_FLOAT; + } + else if ((d > 0 && d < MIN_FLOAT) || (d < 0 && d > -MIN_FLOAT)) + { + warning ("`float' constant truncated to zero"); + *dp = 0.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. */ -/* Output the insns needed to do a call. */ +void +asm_declare_function_name (file, name, decl) + FILE *file; + char *name; + tree decl; +{ + tree parms; + int nargs = list_length (DECL_ARGUMENTS (decl)); -char * -output_call (insn, address, argcount) - rtx insn, address, argcount; + 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; { - int set_ap = TARGET_ARGCOUNT || argcount != const0_rtx; + long u[2]; + REAL_VALUE_TYPE d; - /* If AP is used by the call address, evaluate the address into a temp. */ - if (reg_mentioned_p (arg_pointer_rtx, address)) - if (set_ap) - { - address = XEXP (address, 0); - output_asm_insn ("ld.w %0,a1", &address); - address = gen_rtx (MEM, QImode, gen_rtx (REG, Pmode, 9)); + 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; - /* If there are args, point AP to them. */ - if (set_ap) - output_asm_insn ("mov sp,ap", 0); + 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); + } + } +} - /* If we are passing an arg count, convert it to words and push it. */ - if (TARGET_ARGCOUNT) +/* 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) { - argcount = gen_rtx (CONST_INT, VOIDmode, (INTVAL (argcount) + 3) / 4); - output_asm_insn ("pshea %a0", &argcount); + fprintf (file, "@"); + addr = XEXP (addr, 0); } - /* The call. */ - output_asm_insn ("calls %0", &address); + switch (GET_CODE (addr)) + { + case REG: + index = addr; + break; - /* If we clobbered AP, reload it if it is live. */ - if (set_ap) - if (ap_reload_needed (insn)) - output_asm_insn ("ld.w 12(fp),ap", 0); + 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; - /* If we pushed an arg count, pop it and the args. */ - if (TARGET_ARGCOUNT) - { - argcount = gen_rtx (CONST_INT, VOIDmode, INTVAL (argcount) * 4 + 4); - output_asm_insn ("add.w %0,sp", &argcount); + 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 () +{ + end_sequence (); + replace_arg_pushes_1 (); + start_sequence (); +} + +replace_arg_pushes_1 () +{ + rtx insn, argblock; + int size; + int n; + + /* Look back to see if we are at the return at the end of the function. */ + n = 0; + for (insn = get_last_insn (); ; insn = PREV_INSN (insn)) + if (! insn || ++n > 5) + return; + else if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END) + break; + + /* Yes, we are. Find the max stack depth used by fixable arg pushes. */ + size = replace_pushes (0); + + /* Allocate block in frame to hold all arg lists. */ + argblock = assign_stack_local (BLKmode, size, STACK_BOUNDARY); + + /* Replace pushes with stores into the block. */ + replace_pushes (plus_constant (XEXP (argblock, 0), size)); +} + +int +replace_pushes (arg_addr) + rtx arg_addr; +{ + struct slot_info { rtx insn; int offs; int size; }; +#define MAXSLOTS 1024 + struct slot_info slots[MAXSLOTS]; + rtx insn, pattern, dest; + enum machine_mode mode; + int offs, minoffs; + int nslot, islot; + int args_size, slots_size; + nslot = 0; + offs = 0; + minoffs = 0; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + if (GET_CODE (pattern) == SET) + { + dest = SET_DEST (pattern); + mode = GET_MODE (dest); + if (push_operand (dest, mode)) + { + offs -= + slots[nslot].size = PUSH_ROUNDING (GET_MODE_SIZE (mode)); + slots[nslot].offs = offs; + slots[nslot].insn = insn; + nslot++; + } + else if (dest == stack_pointer_rtx) + { + rtx src = SET_SRC (pattern); + if (GET_CODE (src) == PLUS + && XEXP (src, 0) == stack_pointer_rtx + && GET_CODE (XEXP (src, 1)) == CONST_INT) + { + offs -= + slots[nslot].size = - INTVAL (XEXP (src, 1)); + slots[nslot].offs = 0; + slots[nslot].insn = insn; + nslot++; + } + else + { + slots[nslot].size = 0; + slots[nslot].offs = 0; + slots[nslot].insn = 0; + nslot++; + } + } + else if (reg_mentioned_p (stack_pointer_rtx, pattern)) + { + slots[nslot].size = 0; + slots[nslot].offs = 0; + slots[nslot].insn = 0; + nslot++; + } + else if (reg_mentioned_p (virtual_stack_dynamic_rtx, pattern) + || reg_mentioned_p (virtual_outgoing_args_rtx, pattern)) + { + slots[nslot].size = 0; + slots[nslot].offs = 0; + slots[nslot].insn = 0; + nslot++; + } + } + else + if (reg_mentioned_p (stack_pointer_rtx, pattern) + || reg_mentioned_p (virtual_stack_dynamic_rtx, pattern) + || reg_mentioned_p (virtual_outgoing_args_rtx, pattern) + || reg_mentioned_p (frame_pointer_rtx, pattern)) + abort (); + + break; + + case CALL_INSN: + { + pattern = PATTERN (insn); + if (GET_CODE (pattern) != PARALLEL) + abort (); + pattern = XVECEXP (pattern, 0, 0); + if (GET_CODE (pattern) == SET) + pattern = SET_SRC (pattern); + if (GET_CODE (pattern) != CALL) + abort (); + args_size = INTVAL (XEXP (pattern, 1)); + + slots_size = 0; + for (islot = nslot; islot > 0; islot--) + { + if (slots[islot - 1].insn == 0) + break; + if (slots_size >= args_size) + break; + slots_size += slots[islot - 1].size; + } + + if (slots_size != args_size) + { + offs += args_size; + if (offs > 0) + offs = 0; + slots[nslot].size = 0; + slots[nslot].offs = 0; + slots[nslot].insn = 0; + nslot++; + + if (arg_addr) + { + /* add insn to pop arg list if left on stack */ + rtx pop_size = XVECEXP (PATTERN (insn), 0, 2); + if (pop_size != const0_rtx) + emit_insn_after (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + pop_size), + insn); + insn = NEXT_INSN (insn); + } + break; + } + + /* Record size of arg block */ + if (offs < minoffs) + minoffs = offs; + + /*printf ("call %d, args", INSN_UID (insn));*/ + if (arg_addr) + { + /* store arg block + offset as arg list address for call */ + XVECEXP (PATTERN (insn), 0, 3) = plus_constant (arg_addr, offs); + + /* rewrite arg instructions to use block */ + while (nslot > islot) + { + nslot--; + /*printf (" insn %d size %d offs %d", + INSN_UID(slots[nslot].insn), + slots[nslot].size, + slots[nslot].offs);*/ + + if (slots[nslot].offs == 0) + delete_insn (slots[nslot].insn); + else + { + rtx pattern = PATTERN (slots[nslot].insn); + enum machine_mode mode = GET_MODE (SET_DEST (pattern)); + if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (SImode)) + { + SET_SRC (pattern) = + gen_lowpart (SImode, SET_SRC (pattern)); + SET_DEST (pattern) = + gen_rtx (MEM, SImode, + plus_constant (arg_addr, + slots[nslot].offs)); + } + else + SET_DEST (pattern) = + gen_rtx (MEM, mode, + plus_constant (arg_addr, + slots[nslot].offs)); + } + } + /*printf ("\n");*/ + } + + nslot = islot; + + offs += args_size; + if (offs > 0) + abort (); + } + break; + + case CODE_LABEL: + case JUMP_INSN: + case BARRIER: + nslot = offs = 0; + } + + /*printf ("min offset %d\n", minoffs);*/ + return -minoffs; +} + +/* 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 - address of the arg list. + */ + +char * +output_call (insn, operands) + rtx insn, *operands; +{ + /*if (operands[3] == stack_pointer_rtx) + output_asm_insn ("mov sp,ap"); + else + output_asm_insn ("ldea %a4,ap", operands);*/ + + if (TARGET_ARGCOUNT) + output_asm_insn ("pshea %a2", operands); + + output_asm_insn ("calls %0", operands); + + /*output_asm_insn ("ld.w 12(fp),ap");*/ + + /*if (operands[3] == stack_pointer_rtx && operands[1] != const0_rtx) + output_asm_insn ("add.w %1,sp", operands);*/ + return ""; } + + +/* Here after reloading, before the second scheduling pass. + Insert explicit AP moves. */ + +emit_ap_optimizations () +{ + end_sequence (); + insert_ap_loads (); + start_sequence (); +} + +#define LABEL_DEAD_AP(INSN) ((INSN)->volatil) + +insert_ap_loads () +{ + rtx insn, pattern, src; + int ap_is_live, doagain; + + /* Check that code_label->volatil is not being used for something else */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CODE_LABEL) + if (LABEL_DEAD_AP (insn)) + abort (); + + ap_is_live = 0; + + do + { + doagain = 0; + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + if (! ap_is_live) + { + if (reg_mentioned_p (arg_pointer_rtx, pattern)) + ap_is_live = 1; + } + break; + + case CALL_INSN: + pattern = PATTERN (insn); + if (XVECEXP (pattern, 0, 2) != const0_rtx) + ap_is_live = reg_mentioned_p (arg_pointer_rtx, pattern); + break; + + case CODE_LABEL: + if (! ap_is_live) + { + if (! LABEL_DEAD_AP (insn)) + doagain = 1; + LABEL_DEAD_AP (insn) = 1; + } + break; + + case JUMP_INSN: + pattern = PATTERN (insn); + if (GET_CODE (pattern) == RETURN) + ap_is_live = 0; + else if (JUMP_LABEL (insn)) + { + if (simplejump_p (insn)) + ap_is_live = ! LABEL_DEAD_AP (JUMP_LABEL (insn)); + else if (! ap_is_live && condjump_p (insn)) + ap_is_live = ! LABEL_DEAD_AP (JUMP_LABEL (insn)); + else + ap_is_live = 1; + } + else + ap_is_live = 1; + break; + + case BARRIER: + ap_is_live = 0; + break; + } + } while (doagain); + + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + if (! ap_is_live) + { + if (reg_mentioned_p (arg_pointer_rtx, pattern)) + ap_is_live = 1; + } + break; + + case CALL_INSN: + pattern = PATTERN (insn); + if (XVECEXP (pattern, 0, 2) != const0_rtx) + { + rtx arg_addr = XVECEXP (pattern, 0, 3); + emit_insn_before (gen_movsi (arg_pointer_rtx, arg_addr), insn); + if (ap_is_live) + emit_insn_after (gen_movsi (arg_pointer_rtx, + gen_rtx (MEM, SImode, + gen_rtx (PLUS, Pmode, + frame_pointer_rtx, + GEN_INT (12)))), + insn); + XVECEXP (pattern, 0, 3) = const0_rtx; + insn = PREV_INSN (insn); + ap_is_live = 0; + } + break; + + case CODE_LABEL: + if (ap_is_live != ! LABEL_DEAD_AP (insn)) + abort (); + break; + + case JUMP_INSN: + pattern = PATTERN (insn); + if (GET_CODE (pattern) == RETURN) + ap_is_live = 0; + else if (JUMP_LABEL (insn)) + { + if (simplejump_p (insn)) + ap_is_live = ! LABEL_DEAD_AP (JUMP_LABEL (insn)); + else if (! ap_is_live && condjump_p (insn)) + ap_is_live = ! LABEL_DEAD_AP (JUMP_LABEL (insn)); + else + ap_is_live = 1; + } + else + ap_is_live = 1; + break; + + case BARRIER: + ap_is_live = 0; + break; + } + + /* Clear code-label flag recording dead ap's. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CODE_LABEL) + LABEL_DEAD_AP (insn) = 0; +} |