This is out-ns32k.c in view mode; [Download] [Up]
/* Subroutines for assembler code output on the NS32000. Copyright (C) 1988 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. */ /* Some output-actions in ns32k.md need these. */ #include <stdio.h> extern FILE *asm_out_file; #define FP_REG_P(X) (GET_CODE (X) == REG && REGNO (X) > 7 && REGNO (X) < 16) /* Generate the rtx that comes from an address expression in the md file */ /* The expression to be build is BASE[INDEX:SCALE]. To recognize this, scale must be converted from an exponent (from ASHIFT) to a muliplier (for MULT). */ rtx gen_indexed_expr (base, index, scale) rtx base, index, scale; { rtx addr; /* This generates an illegal addressing mode, if BASE is fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */ if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT) base = gen_rtx (MEM, SImode, base); addr = gen_rtx (MULT, SImode, index, gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale))); addr = gen_rtx (PLUS, SImode, base, addr); return addr; } /* Return 1 if OP is a valid constant int. These can be modeless (void mode), so we do not mess with their modes. The main use of this function is as a predicate in match_operand expressions in the machine description. */ int const_int (op, mode) register rtx op; enum machine_mode mode; { return (GET_CODE (op) == CONST_INT); } /* Return 1 if OP is a valid operand of mode MODE. This predicate rejects operands which do not have a mode (such as CONST_INT which are VOIDmode). */ int reg_or_mem_operand (op, mode) register rtx op; enum machine_mode mode; { return (GET_MODE (op) == mode && (GET_CODE (op) == REG || GET_CODE (op) == SUBREG || GET_CODE (op) == MEM)); } /* Return the best assembler insn template for moving operands[1] into operands[0] as a fullword. */ static char * singlemove_string (operands) rtx *operands; { if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) <= 7 && INTVAL (operands[1]) >= -8) return "movqd %1,%0"; return "movd %1,%0"; } char * output_move_double (operands) rtx *operands; { enum anon1 { REGOP, OFFSOP, POPOP, CNSTOP, RNDOP } optype0, optype1; rtx latehalf[2]; /* First classify both operands. */ if (REG_P (operands[0])) optype0 = REGOP; else if (offsettable_memref_p (operands[0])) optype0 = OFFSOP; else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) optype0 = POPOP; else optype0 = RNDOP; if (REG_P (operands[1])) optype1 = REGOP; else if (CONSTANT_ADDRESS_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE) optype1 = CNSTOP; else if (offsettable_memref_p (operands[1])) optype1 = OFFSOP; else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) optype1 = POPOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP) abort (); /* Ok, we can do one word at a time. Normally we do the low-numbered word first, but if either operand is autodecrementing then we do the high-numbered word first. In either case, set up in LATEHALF the operands to use for the high-numbered word and in some cases alter the operands in OPERANDS to be suitable for the low-numbered word. */ if (optype0 == REGOP) latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); else if (optype0 == OFFSOP) latehalf[0] = adj_offsettable_operand (operands[0], 4); else latehalf[0] = operands[0]; if (optype1 == REGOP) latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (operands[1], 4); else if (optype1 == CNSTOP) { if (CONSTANT_ADDRESS_P (operands[1])) latehalf[1] = const0_rtx; else if (GET_CODE (operands[1]) == CONST_DOUBLE) { latehalf[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[1])); operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[1])); } } else latehalf[1] = operands[1]; /* If one or both operands autodecrementing, do the two words, high-numbered first. */ if (optype0 == POPOP || optype1 == POPOP) { output_asm_insn (singlemove_string (latehalf), latehalf); return singlemove_string (operands); } /* Not autodecrementing. Do the two words, low-numbered first. */ output_asm_insn (singlemove_string (operands), operands); operands[0] = latehalf[0]; operands[1] = latehalf[1]; return singlemove_string (operands); } int check_reg (oper, reg) rtx oper; int reg; { register int i; if (oper == 0) return 0; switch (GET_CODE(oper)) { case REG: return (REGNO(oper) == reg) ? 1 : 0; case MEM: return check_reg(XEXP(oper, 0), reg); case PLUS: case MULT: return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg); } return 0; } /* PRINT_OPERAND_ADDRESS is defined to call this function, which is easier to debug than putting all the code in a macro definition in tm-ns32k.h . */ /* Nonzero if we have printed a base register. If zero, on some systems, it means `(sb)' must be printed. */ int paren_base_reg_printed = 0; print_operand_address (file, addr) register FILE *file; register rtx addr; { register rtx reg1, reg2, breg, ireg; rtx offset; static char scales[] = { 'b', 'w', 'd', 0, 'q', }; retry: switch (GET_CODE (addr)) { case MEM: addr = XEXP (addr, 0); if (GET_CODE (addr) == REG) if (REGNO (addr) == STACK_POINTER_REGNUM) { fprintf (file, "tos"); break; } else { fprintf (file, "%s", reg_names[REGNO (addr)]); break; } else if (CONSTANT_P (addr)) { output_addr_const (file, addr); break; } else if (GET_CODE (addr) == MULT) { fprintf (file, "@0"); ireg = addr; goto print_index; } else if (GET_CODE (addr) == MEM) { addr = XEXP (addr, 0); if (GET_CODE (addr) == PLUS) { offset = XEXP (addr, 1); addr = XEXP (addr, 0); } else { offset = const0_rtx; } output_addr_const (file, offset); fprintf (file, "(%s)", reg_names[REGNO (addr)]); break; } if (GET_CODE (addr) != PLUS) abort (); goto retry; case REG: if (REGNO (addr) == STACK_POINTER_REGNUM) fprintf (file, "tos"); else fprintf (file, "0(%s)", reg_names[REGNO (addr)]); break; case PRE_DEC: case POST_INC: fprintf (file, "tos"); break; case MULT: fprintf (file, "@0"); ireg = addr; /* [rX:Y] */ goto print_index; break; case PLUS: reg1 = 0; reg2 = 0; ireg = 0; breg = 0; offset = const0_rtx; if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) { offset = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) { offset = XEXP (addr, 1); addr = XEXP (addr, 0); } if (GET_CODE (addr) != PLUS) ; else if (GET_CODE (XEXP (addr, 0)) == MULT) { reg1 = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (GET_CODE (XEXP (addr, 1)) == MULT) { reg1 = XEXP (addr, 1); addr = XEXP (addr, 0); } /* The case for memory is somewhat tricky: to get a MEM here, the only RTX formats that could get here are either (modulo commutativity) (PLUS (PLUS (REG *MEM)) CONST) -or- (PLUS (PLUS (CONST REG/MULT)) *MEM) We take advantage of that knowledge here. */ else if (GET_CODE (XEXP (addr, 0)) == MEM || GET_CODE (XEXP (addr, 1)) == MEM) { rtx temp; if (GET_CODE (XEXP (addr, 0)) == MEM) { temp = XEXP (addr, 1); addr = XEXP (addr, 0); } else { temp = XEXP (addr, 0); addr = XEXP (addr, 1); } if (GET_CODE (temp) == REG) { reg1 = temp; } else { if (GET_CODE (temp) != PLUS) abort (); if (GET_CODE (XEXP (temp, 0)) == MULT) { reg1 = XEXP (temp, 0); offset = XEXP (temp, 1); } if (GET_CODE (XEXP (temp, 1)) == MULT) { reg1 = XEXP (temp, 1); offset = XEXP (temp, 0); } else abort (); } } else if (GET_CODE (XEXP (addr, 0)) == REG || GET_CODE (XEXP (addr, 1)) == REG) { rtx temp; if (GET_CODE (XEXP (addr, 0)) == REG) { temp = XEXP (addr, 0); addr = XEXP (addr, 1); } else { temp = XEXP (addr, 1); addr = XEXP (addr, 0); } if (GET_CODE (addr) == REG) { if (REGNO (temp) >= FRAME_POINTER_REGNUM) { reg1 = addr; addr = temp; } else { reg1 = temp; } } else if (CONSTANT_P (addr)) { if (GET_CODE (offset) == CONST_INT && INTVAL (offset)) offset = plus_constant (addr, INTVAL (offset)); addr = temp; } else if (GET_CODE (addr) != PLUS) abort (); else { if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) { offset = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) { offset = XEXP (addr, 1); addr = XEXP (addr, 0); } else abort (); if (GET_CODE (addr) == REG) { if (REGNO (temp) >= FRAME_POINTER_REGNUM) { reg1 = addr; addr = temp; } else { reg1 = temp; } } else reg1 = temp; } } if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) { if (reg1 == 0) reg1 = addr; else reg2 = addr; addr = 0; } if (addr != 0) { if (CONSTANT_P (addr) && reg1) { /* OFFSET comes second, to prevent outputting operands of the form INT+SYMBOL+INT. The Genix assembler dies on them. */ output_addr_const (file, addr); if (offset != const0_rtx) { putc ('+', file); output_addr_const (file, offset); } ireg = reg1; goto print_index; } else if (GET_CODE (addr) != MEM) abort (); output_addr_const (file, offset); #ifndef SEQUENT_ADDRESS_BUG putc ('(', file); paren_base_reg_printed = 0; output_address (addr); #ifdef SEQUENT_BASE_REGS if (!paren_base_reg_printed) fprintf (file, "(sb)"); #endif putc (')', file); #else /* SEQUENT_ADDRESS_BUG */ if ((GET_CODE (offset) == SYMBOL_REF || GET_CODE (offset) == CONST) && GET_CODE (addr) == REG) { if (reg1) abort (); fprintf (file, "[%s:b]", reg_names[REGNO (addr)]); } else { putc ('(', file); paren_base_reg_printed = 0; output_address (addr); #ifdef SEQUENT_BASE_REGS if (!paren_base_reg_printed) fprintf (file, "(sb)"); #endif putc (')', file); } #endif /* SEQUENT_ADDRESS_BUG */ ireg = reg1; goto print_index; } else addr = offset; if (reg1 && GET_CODE (reg1) == MULT) { breg = reg2; ireg = reg1; } else if (reg2 && GET_CODE (reg2) == MULT) { breg = reg1; ireg = reg2; } else if (reg2 || GET_CODE (addr) == MEM) { breg = reg2; ireg = reg1; } else { breg = reg1; ireg = reg2; } if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF) { int scale; if (GET_CODE (ireg) == MULT) { scale = INTVAL (XEXP (ireg, 1)) >> 1; ireg = XEXP (ireg, 0); } else scale = 0; output_asm_label (addr); fprintf (file, "[%s:%c]", reg_names[REGNO (ireg)], scales[scale]); break; } if (ireg && breg && offset == const0_rtx) fprintf (file, "0(%s)", reg_names[REGNO (breg)]); else { if (addr != 0) { if (ireg != 0 && breg == 0 && GET_CODE (offset) == CONST_INT) putc('@', file); output_addr_const (file, offset); } if (breg != 0) { if (GET_CODE (breg) != REG) abort (); #ifndef SEQUENT_ADDRESS_BUG fprintf (file, "(%s)", reg_names[REGNO (breg)]); paren_base_reg_printed = -1; #else if (GET_CODE (offset) == SYMBOL_REF || GET_CODE (offset) == CONST) { if (ireg) abort (); fprintf (file, "[%s:b]", reg_names[REGNO (breg)]); } else { fprintf (file, "(%s)", reg_names[REGNO (breg)]); paren_base_reg_printed = -1; } #endif } } print_index: if (ireg != 0) { int scale; if (GET_CODE (ireg) == MULT) { scale = INTVAL (XEXP (ireg, 1)) >> 1; ireg = XEXP (ireg, 0); } else scale = 0; if (GET_CODE (ireg) != REG) abort (); fprintf (file, "[%s:%c]", reg_names[REGNO (ireg)], scales[scale]); } break; default: output_addr_const (file, addr); } } /* National 32032 shifting is so bad that we can get better performance in many common cases by using other techniques. */ char * output_shift_insn (operands) rtx *operands; { if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) > 0 && INTVAL (operands[2]) <= 3) if (GET_CODE (operands[0]) == REG) { if (GET_CODE (operands[1]) == REG) { if (REGNO (operands[0]) == REGNO (operands[1])) { if (operands[2] == const1_rtx) return "addd %0,%0"; else if (INTVAL (operands[2]) == 2) return "addd %0,%0\n\taddd %0,%0"; } if (operands[2] == const1_rtx) return "movd %1,%0\n\taddd %0,%0"; operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); return "addr %a1,%0"; } if (operands[2] == const1_rtx) return "movd %1,%0\n\taddd %0,%0"; } else if (GET_CODE (operands[1]) == REG) { operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); return "addr %a1,%0"; } else if (INTVAL (operands[2]) == 1 && GET_CODE (operands[1]) == MEM && rtx_equal_p (operands [0], operands[1])) { rtx temp = XEXP (operands[1], 0); if (GET_CODE (temp) == REG || (GET_CODE (temp) == PLUS && GET_CODE (XEXP (temp, 0)) == REG && GET_CODE (XEXP (temp, 1)) == CONST_INT)) return "addd %0,%0"; } else return "ashd %2,%0"; return "ashd %2,%0"; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.