/* Subroutines for insn-output.c for Convex. Copyright (C) 1989,1991 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 "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" /* 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; 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 (); return ""; } char * gen_cmp (label, cmpop, tf) rtx label; char *cmpop; char tf; { char buf[80]; char revop[4]; rtx ops[3]; ops[2] = label; /* 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)) { ops[0] = xop1; ops[1] = xop0; if (cmpop[0] == 'l') { bcopy (cmpop, revop, sizeof revop); revop[1] ^= 'e' ^ 't'; tf ^= 't' ^ 'f'; cmpop = revop; } } else { ops[0] = xop0; ops[1] = xop1; } sprintf (buf, "%s.%c %%0,%%1\n\tjbr%c.%c %%l2", cmpop, typech, regch, tf); output_asm_insn (buf, ops); return ""; } /* 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); } /* Return the number of args in the call insn X. */ static int call_num_args (x) rtx x; { if (GET_CODE (x) == CALL) return INTVAL (x->fld[1].rtx); if (GET_CODE (x) == SET) return call_num_args (SET_SRC (x)); abort (); } /* 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 int ap_reload_needed (insn) rtx insn; { for (;;) { insn = NEXT_INSN (insn); switch (GET_CODE (insn)) { 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; } } } /* Output the insns needed to do a call. */ char * output_call (insn, address, argcount) rtx insn, address, argcount; { int set_ap = TARGET_ARGCOUNT || argcount != const0_rtx; /* 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)); } /* If there are args, point AP to them. */ if (set_ap) output_asm_insn ("mov sp,ap"); /* If we are passing an arg count, convert it to words and push it. */ if (TARGET_ARGCOUNT) { argcount = gen_rtx (CONST_INT, VOIDmode, (INTVAL (argcount) + 3) / 4); output_asm_insn ("pshea %a0", &argcount); } /* The call. */ output_asm_insn ("calls %0", &address); /* 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"); /* 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); } return ""; }