📄 mn10300.c
字号:
/* Subroutines for insn-output.c for Matsushita MN10300 series Copyright (C) 1996, 1997 Free Software Foundation, Inc. Contributed by Jeff Law (law@cygnus.com).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. */#include "config.h"#include <stdio.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 "flags.h"#include "recog.h"#include "expr.h"#include "tree.h"#include "obstack.h"/* Global registers known to hold the value zero. Normally we'd depend on CSE and combine to put zero into a register and re-use it. However, on the mn10x00 processors we implicitly use the constant zero in tst instructions, so we might be able to do better by loading the value into a register in the prologue, then re-useing that register throughout the function. We could perform similar optimizations for other constants, but with gcse due soon, it doesn't seem worth the effort. These variables hold a rtx for a register known to hold the value zero throughout the entire function, or NULL if no register of the appropriate class has such a value throughout the life of the function. */rtx zero_dreg;rtx zero_areg;voidasm_file_start (file) FILE *file;{ fprintf (file, "#\tGCC For the Matsushita MN10300\n"); if (optimize) fprintf (file, "# -O%d\n", optimize); else fprintf (file, "\n\n"); output_file_directive (file, main_input_filename);}/* Print operand X using operand code CODE to assembly language output file FILE. */voidprint_operand (file, x, code) FILE *file; rtx x; int code;{ switch (code) { case 'b': case 'B': /* These are normal and reversed branches. */ switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x))) { case NE: fprintf (file, "ne"); break; case EQ: fprintf (file, "eq"); break; case GE: fprintf (file, "ge"); break; case GT: fprintf (file, "gt"); break; case LE: fprintf (file, "le"); break; case LT: fprintf (file, "lt"); break; case GEU: fprintf (file, "cc"); break; case GTU: fprintf (file, "hi"); break; case LEU: fprintf (file, "ls"); break; case LTU: fprintf (file, "cs"); break; default: abort (); } break; case 'C': /* This is used for the operand to a call instruction; if it's a REG, enclose it in parens, else output the operand normally. */ if (GET_CODE (x) == REG) { fputc ('(', file); print_operand (file, x, 0); fputc (')', file); } else print_operand (file, x, 0); break; /* These are the least significant word in a 64bit value. */ case 'L': switch (GET_CODE (x)) { case MEM: fputc ('(', file); output_address (XEXP (x, 0)); fputc (')', file); break; case REG: fprintf (file, "%s", reg_names[REGNO (x)]); break; case SUBREG: fprintf (file, "%s", reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)]); break; case CONST_DOUBLE: { long val[2]; REAL_VALUE_TYPE rv; switch (GET_MODE (x)) { case DFmode: REAL_VALUE_FROM_CONST_DOUBLE (rv, x); REAL_VALUE_TO_TARGET_DOUBLE (rv, val); print_operand_address (file, GEN_INT (val[0])); break;; case SFmode: REAL_VALUE_FROM_CONST_DOUBLE (rv, x); REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]); print_operand_address (file, GEN_INT (val[0])); break;; case VOIDmode: case DImode: print_operand_address (file, GEN_INT (CONST_DOUBLE_LOW (x))); break; } break; } case CONST_INT: print_operand_address (file, x); break; default: abort (); } break; /* Similarly, but for the most significant word. */ case 'H': switch (GET_CODE (x)) { case MEM: fputc ('(', file); x = adj_offsettable_operand (x, 4); output_address (XEXP (x, 0)); fputc (')', file); break; case REG: fprintf (file, "%s", reg_names[REGNO (x) + 1]); break; case SUBREG: fprintf (file, "%s", reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)] + 1); break; case CONST_DOUBLE: { long val[2]; REAL_VALUE_TYPE rv; switch (GET_MODE (x)) { case DFmode: REAL_VALUE_FROM_CONST_DOUBLE (rv, x); REAL_VALUE_TO_TARGET_DOUBLE (rv, val); print_operand_address (file, GEN_INT (val[1])); break;; case SFmode: abort (); case VOIDmode: case DImode: print_operand_address (file, GEN_INT (CONST_DOUBLE_HIGH (x))); break; } break; } case CONST_INT: if (INTVAL (x) < 0) print_operand_address (file, GEN_INT (-1)); else print_operand_address (file, GEN_INT (0)); break; default: abort (); } break; case 'A': fputc ('(', file); if (GET_CODE (XEXP (x, 0)) == REG) output_address (gen_rtx (PLUS, SImode, XEXP (x, 0), GEN_INT (0))); else output_address (XEXP (x, 0)); fputc (')', file); break; case 'N': output_address (GEN_INT ((~INTVAL (x)) & 0xff)); break; /* For shift counts. The hardware ignores the upper bits of any immediate, but the assembler will flag an out of range shift count as an error. So we mask off the high bits of the immediate here. */ case 'S': if (GET_CODE (x) == CONST_INT) { fprintf (file, "%d", INTVAL (x) & 0x1f); break; } /* FALL THROUGH */ default: switch (GET_CODE (x)) { case MEM: fputc ('(', file); output_address (XEXP (x, 0)); fputc (')', file); break; case PLUS: output_address (x); break; case REG: fprintf (file, "%s", reg_names[REGNO (x)]); break; case SUBREG: fprintf (file, "%s", reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)]); break; /* This will only be single precision.... */ case CONST_DOUBLE: { unsigned long val; REAL_VALUE_TYPE rv; REAL_VALUE_FROM_CONST_DOUBLE (rv, x); REAL_VALUE_TO_TARGET_SINGLE (rv, val); print_operand_address (file, GEN_INT (val)); break; } case CONST_INT: case SYMBOL_REF: case CONST: case LABEL_REF: case CODE_LABEL: print_operand_address (file, x); break; default: abort (); } break; }}/* Output assembly language output for the address ADDR to FILE. */voidprint_operand_address (file, addr) FILE *file; rtx addr;{ switch (GET_CODE (addr)) { case REG: if (addr == stack_pointer_rtx) print_operand_address (file, gen_rtx (PLUS, SImode, stack_pointer_rtx, GEN_INT (0))); else print_operand (file, addr, 0); break; case PLUS: { rtx base, index; if (REG_P (XEXP (addr, 0)) && REG_OK_FOR_BASE_P (XEXP (addr, 0))) base = XEXP (addr, 0), index = XEXP (addr, 1); else if (REG_P (XEXP (addr, 1)) && REG_OK_FOR_BASE_P (XEXP (addr, 1))) base = XEXP (addr, 1), index = XEXP (addr, 0); else abort (); print_operand (file, index, 0); fputc (',', file); print_operand (file, base, 0);; break; } case SYMBOL_REF: output_addr_const (file, addr); break; default: output_addr_const (file, addr); break; }}intcan_use_return_insn (){ /* size includes the fixed stack space needed for function calls. */ int size = get_frame_size () + current_function_outgoing_args_size; /* And space for the return pointer. */ size += current_function_outgoing_args_size ? 4 : 0; return (reload_completed && size == 0 && !regs_ever_live[2] && !regs_ever_live[3] && !regs_ever_live[6] && !regs_ever_live[7] && !frame_pointer_needed);}/* Count the number of tst insns which compare a data or address register with zero. */static void count_tst_insns (dreg_countp, areg_countp) int *dreg_countp; int *areg_countp;{ rtx insn; /* Assume no tst insns exist. */ *dreg_countp = 0; *areg_countp = 0; /* If not optimizing, then quit now. */ if (!optimize) return; /* Walk through all the insns. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { rtx pat; /* Ignore anything that is not a normal INSN. */ if (GET_CODE (insn) != INSN) continue; /* Ignore anything that isn't a SET. */ pat = PATTERN (insn); if (GET_CODE (pat) != SET) continue; /* Check for a tst insn. */ if (SET_DEST (pat) == cc0_rtx && GET_CODE (SET_SRC (pat)) == REG) { if (REGNO_REG_CLASS (REGNO (SET_SRC (pat))) == DATA_REGS) (*dreg_countp)++; if (REGNO_REG_CLASS (REGNO (SET_SRC (pat))) == ADDRESS_REGS) (*areg_countp)++; } /* Setting an address register to zero can also be optimized, so count it just like a tst insn. */ if (GET_CODE (SET_DEST (pat)) == REG && GET_CODE (SET_SRC (pat)) == CONST_INT && INTVAL (SET_SRC (pat)) == 0 && REGNO_REG_CLASS (REGNO (SET_DEST (pat))) == ADDRESS_REGS) (*areg_countp)++; }}voidexpand_prologue (){ unsigned int size; /* We need to end the current sequence so that count_tst_insns can look at all the insns in this function. Normally this would be unsafe, but it's OK in the prologue/epilogue expanders. */ end_sequence (); /* Determine if it is profitable to put the value zero into a register for the entire function. If so, set ZERO_DREG and ZERO_AREG. */ if (regs_ever_live[2] || regs_ever_live[3] || regs_ever_live[6] || regs_ever_live[7] || frame_pointer_needed) { int dreg_count, areg_count; /* Get a count of the number of tst insns which use address and data registers. */ count_tst_insns (&dreg_count, &areg_count); /* If there's more than one tst insn using a data register, then this optimization is a win. */ if (dreg_count > 1 && (!regs_ever_live[2] || !regs_ever_live[3])) { if (!regs_ever_live[2]) { regs_ever_live[2] = 1; zero_dreg = gen_rtx (REG, SImode, 2); } else { regs_ever_live[3] = 1; zero_dreg = gen_rtx (REG, SImode, 3); } } else zero_dreg = NULL_RTX; /* If there's more than two tst insns using an address register, then this optimization is a win. */ if (areg_count > 2 && (!regs_ever_live[6] || !regs_ever_live[7])) { if (!regs_ever_live[6]) { regs_ever_live[6] = 1; zero_areg = gen_rtx (REG, SImode, 6); } else { regs_ever_live[7] = 1; zero_areg = gen_rtx (REG, SImode, 7); } } else zero_areg = NULL_RTX; } else { zero_dreg = NULL_RTX; zero_areg = NULL_RTX; } /* Start a new sequence. */ start_sequence (); /* SIZE includes the fixed stack space needed for function calls. */ size = get_frame_size () + current_function_outgoing_args_size; size += (current_function_outgoing_args_size ? 4 : 0); /* If this is an old-style varargs function, then its arguments need to be flushed back to the stack. */ if (current_function_varargs) { emit_move_insn (gen_rtx (MEM, SImode, gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (4))), gen_rtx (REG, SImode, 0)); emit_move_insn (gen_rtx (MEM, SImode, gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (8))), gen_rtx (REG, SImode, 1)); } /* And now store all the registers onto the stack with a single two byte instruction. */ if (regs_ever_live[2] || regs_ever_live[3] || regs_ever_live[6] || regs_ever_live[7] || frame_pointer_needed) emit_insn (gen_store_movm ()); /* Now put the frame pointer into the frame pointer register. */ if (frame_pointer_needed) emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); /* Allocate stack for this frame. */ if (size) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-size))); /* Load zeros into registers as needed. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -