📄 ns32k.c
字号:
/* Subroutines for assembler code output on the NS32000. Copyright (C) 1988, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. *//* Some output-actions in ns32k.md need these. */#include "config.h"#include "system.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"#include "tree.h"#include "expr.h"#include "flags.h"#ifdef OSF_OSint ns32k_num_files = 0;#endif/* This duplicates reg_class_contens in reg_class.c, but maybe that isn't initialized in time. Also this is more convenient as an array of ints. We know that HARD_REG_SET fits in an unsigned int */unsigned int ns32k_reg_class_contents[N_REG_CLASSES] = REG_CLASS_CONTENTS;enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] ={ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FRAME_POINTER_REG, STACK_POINTER_REG};char *ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES;voidtrace (s, s1, s2) char *s, *s1, *s2;{ fprintf (stderr, s, s1, s2);}/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ inthard_regno_mode_ok (regno, mode) int regno; enum machine_mode mode;{ int size = GET_MODE_UNIT_SIZE(mode); if (FLOAT_MODE_P(mode)) { if (size == UNITS_PER_WORD && regno < L1_REGNUM) return 1; if (size == UNITS_PER_WORD * 2 && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM))) return 1; return 0; } if (size == UNITS_PER_WORD * 2 && (regno & 1) == 0 && regno < F0_REGNUM) return 1; if (size <= UNITS_PER_WORD && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM || regno == STACK_POINTER_REGNUM)) return 1; return 0;}int register_move_cost(CLASS1, CLASS2) enum reg_class CLASS1; enum reg_class CLASS2;{ if (CLASS1 == NO_REGS || CLASS2 == NO_REGS) return 2; if((SUBSET_P(CLASS1, FP_REGS) && !SUBSET_P(CLASS2, FP_REGS)) || (!SUBSET_P(CLASS1, FP_REGS) && SUBSET_P(CLASS2, FP_REGS))) return 8; if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P(CLASS2,GENERAL_REGS)) || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P(CLASS1,GENERAL_REGS))) return 6; if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P(CLASS2,GENERAL_REGS)) || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P(CLASS1,GENERAL_REGS))) return 6; return 2;}#if 0/* We made the insn definitions copy from floating point to general registers via the stack. */int secondary_memory_needed(CLASS1, CLASS2, M) enum reg_class CLASS1; enum reg_class CLASS2; enum machine_mode M;{ int ret = ((SUBSET_P(CLASS1, FP_REGS) && !SUBSET_P(CLASS2, FP_REGS)) || (!SUBSET_P(CLASS1, FP_REGS) && SUBSET_P(CLASS2, FP_REGS))); return ret;}#endif /* ADDRESS_COST calls this. This function is not optimal for the 32032 & 32332, but it probably is better than the default. */intcalc_address_cost (operand) rtx operand;{ int i; int cost = 0; if (GET_CODE (operand) == MEM) cost += 3; if (GET_CODE (operand) == MULT) cost += 2;#if 0 if (GET_CODE (operand) == REG) cost += 1; /* not really, but the documentation says different amount of registers shouldn't return the same costs */#endif switch (GET_CODE (operand)) { case REG: case CONST: case CONST_INT: case CONST_DOUBLE: case SYMBOL_REF: case LABEL_REF: case POST_DEC: case PRE_DEC: break; case MEM: cost += calc_address_cost (XEXP (operand, 0)); break; case MULT: case PLUS: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) { cost += calc_address_cost (XEXP (operand, i)); } default: break; } return cost;}/* Return the register class of a scratch register needed to copy IN into or out of a register in CLASS in MODE. If it can be done directly, NO_REGS is returned. */enum reg_classsecondary_reload_class (class, mode, in) enum reg_class class; enum machine_mode mode; rtx in;{ int regno = true_regnum (in); if (regno >= FIRST_PSEUDO_REGISTER) regno = -1; if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM) || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM)) return GENERAL_REGS; else return NO_REGS;}/* 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 multiplier (for MULT). */static rtxgen_indexed_expr (base, index, scale) rtx base, index, scale;{ rtx addr; /* This generates an invalid 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_INT (1 << INTVAL (scale))); addr = gen_rtx (PLUS, SImode, base, addr); return addr;}/* 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). */intreg_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));}/* Split one or more DImode RTL references into pairs of SImode references. The RTL can be REG, offsettable MEM, integer constant, or CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to split and "num" is its length. lo_half and hi_half are output arrays that parallel "operands". */voidsplit_di (operands, num, lo_half, hi_half) rtx operands[]; int num; rtx lo_half[], hi_half[];{ while (num--) { if (GET_CODE (operands[num]) == REG) { lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num])); hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1); } else if (CONSTANT_P (operands[num])) { split_double (operands[num], &lo_half[num], &hi_half[num]); } else if (offsettable_memref_p (operands[num])) { lo_half[num] = operands[num]; hi_half[num] = adj_offsettable_operand (operands[num], 4); } else abort(); }}/* 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, PUSHOP, 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 = PUSHOP; else optype0 = RNDOP; if (REG_P (operands[1])) optype1 = REGOP; else if (CONSTANT_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 = PUSHOP; 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) split_double (operands[1], &operands[1], &latehalf[1]); else latehalf[1] = operands[1]; /* If insn is effectively movd N(sp),tos then we will do the high word first. We should use the adjusted operand 1 (which is N+4(sp)) for the low word as well, to compensate for the first decrement of sp. Given this, it doesn't matter which half we do "first". */ if (optype0 == PUSHOP && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) operands[1] = latehalf[1]; /* If one or both operands autodecrementing, do the two words, high-numbered first. */ else if (optype0 == PUSHOP || optype1 == PUSHOP) { output_asm_insn (singlemove_string (latehalf), latehalf); return singlemove_string (operands); } /* If the first move would clobber the source of the second one, do them in the other order. */ /* Overlapping registers. */ if (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1])) { /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Do low-numbered word. */ return singlemove_string (operands); } /* Loading into a register which overlaps a register used in the address. */ else if (optype0 == REGOP && optype1 != REGOP && reg_overlap_mentioned_p (operands[0], operands[1])) { if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) { /* If both halves of dest are used in the src memory address, load the destination address into the low reg (operands[0]). Then it works to load latehalf first. */ rtx xops[2]; xops[0] = XEXP (operands[1], 0); xops[1] = operands[0]; output_asm_insn ("addr %a0,%1", xops); operands[1] = gen_rtx (MEM, DImode, operands[0]); latehalf[1] = adj_offsettable_operand (operands[1], 4); /* The first half has the overlap, Do the late half first. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Then clobber. */ return singlemove_string (operands); } if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) { /* The first half has the overlap, Do the late half first. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Then clobber. */ return singlemove_string (operands); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -