📄 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"/* The size of the callee register save area. Right now we save everything on entry since it costs us nothing in code size. It does cost us from a speed standpoint, so we want to optimize this sooner or later. */#define REG_SAVE_BYTES (4 * regs_ever_live[2] \ + 4 * regs_ever_live[3] \ + 4 * regs_ever_live[6] \ + 4 * regs_ever_live[7])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);}voidexpand_prologue (){ unsigned int size; /* 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)));}voidexpand_epilogue (){ unsigned int size; /* 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); /* Maybe cut back the stack, except for the register save area. If the frame pointer exists, then use the frame pointer to cut back the stack. If the stack size + register save area is more than 255 bytes, then the stack must be cut back here since the size + register save size is too big for a ret/retf instruction. Else leave it alone, it will be cut back as part of the ret/retf instruction, or there wasn't any stack to begin with. Under no circumstanes should the register save area be deallocated here, that would leave a window where an interrupt could occur and trash the register save area. */ if (frame_pointer_needed) { emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); size = 0; } else if ((regs_ever_live[2] || regs_ever_live[3] || regs_ever_live[6] || regs_ever_live[7]) && size + REG_SAVE_BYTES > 255) { emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (size))); size = 0; } /* For simplicity, we just movm all the callee saved registers to the stack with one instruction. ?!? Only save registers which are actually used. Reduces stack requirements and is faster. */ if (regs_ever_live[2] || regs_ever_live[3] || regs_ever_live[6] || regs_ever_live[7] || frame_pointer_needed) emit_jump_insn (gen_return_internal_regs (GEN_INT (size + REG_SAVE_BYTES))); else { if (size) { emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (size))); emit_jump_insn (gen_return_internal ()); } else { emit_jump_insn (gen_return ()); } }}/* Update the condition code from the insn. */voidnotice_update_cc (body, insn) rtx body; rtx insn;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -