📄 i386.c
字号:
/* Subroutines for insn-output.c for Intel X86. Copyright (C) 1988, 1992, 1994, 1995 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. */#include <stdio.h>#include <setjmp.h>#include <ctype.h>#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"#include "tree.h"#include "flags.h"#include "function.h"#ifdef EXTRA_CONSTRAINT/* If EXTRA_CONSTRAINT is defined, then the 'S' constraint in REG_CLASS_FROM_LETTER will no longer work, and various asm statements that need 'S' for class SIREG will break. */ error EXTRA_CONSTRAINT conflicts with S constraint letter/* The previous line used to be #error, but some compilers barf even if the conditional was untrue. */#endif#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))extern FILE *asm_out_file;extern char *strcat ();char *singlemove_string ();char *output_move_const_single ();char *output_fp_cc0_set ();char *hi_reg_name[] = HI_REGISTER_NAMES;char *qi_reg_name[] = QI_REGISTER_NAMES;char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;/* Array of the smallest class containing reg number REGNO, indexed by REGNO. Used by REGNO_REG_CLASS in i386.h. */enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] ={ /* ax, dx, cx, bx */ AREG, DREG, CREG, BREG, /* si, di, bp, sp */ SIREG, DIREG, INDEX_REGS, GENERAL_REGS, /* FP registers */ FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, /* arg pointer */ INDEX_REGS};/* Test and compare insns in i386.md store the information needed to generate branch and scc insns here. */struct rtx_def *i386_compare_op0 = NULL_RTX;struct rtx_def *i386_compare_op1 = NULL_RTX;struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();/* Register allocation order */char *i386_reg_alloc_order;static char regs_allocated[FIRST_PSEUDO_REGISTER];/* # of registers to use to pass arguments. */char *i386_regparm_string; /* # registers to use to pass args */int i386_regparm; /* i386_regparm_string as a number *//* Alignment to use for loops and jumps */char *i386_align_loops_string; /* power of two alignment for loops */char *i386_align_jumps_string; /* power of two alignment for non-loop jumps */char *i386_align_funcs_string; /* power of two alignment for functions */int i386_align_loops; /* power of two alignment for loops */int i386_align_jumps; /* power of two alignment for non-loop jumps */int i386_align_funcs; /* power of two alignment for functions *//* Sometimes certain combinations of command options do not make sense on a particular target machine. You can define a macro `OVERRIDE_OPTIONS' to take account of this. This macro, if defined, is executed once just after all the command options have been parsed. Don't use this macro to turn on various extra optimizations for `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */voidoverride_options (){ int ch, i, regno; char *p; int def_align;#ifdef SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS;#endif /* Validate registers in register allocation order */ if (i386_reg_alloc_order) { for (i = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++) { switch (ch) { case 'a': regno = 0; break; case 'd': regno = 1; break; case 'c': regno = 2; break; case 'b': regno = 3; break; case 'S': regno = 4; break; case 'D': regno = 5; break; case 'B': regno = 6; break; default: fatal ("Register '%c' is unknown", ch); } if (regs_allocated[regno]) fatal ("Register '%c' was already specified in the allocation order", ch); regs_allocated[regno] = 1; } } /* Validate -mregparm= value */ if (i386_regparm_string) { i386_regparm = atoi (i386_regparm_string); if (i386_regparm < 0 || i386_regparm > REGPARM_MAX) fatal ("-mregparm=%d is not between 0 and %d", i386_regparm, REGPARM_MAX); } def_align = (TARGET_386) ? 2 : 4; /* Validate -malign-loops= value, or provide default */ if (i386_align_loops_string) { i386_align_loops = atoi (i386_align_loops_string); if (i386_align_loops < 0 || i386_align_loops > MAX_CODE_ALIGN) fatal ("-malign-loops=%d is not between 0 and %d", i386_align_loops, MAX_CODE_ALIGN); } else i386_align_loops = 2; /* Validate -malign-jumps= value, or provide default */ if (i386_align_jumps_string) { i386_align_jumps = atoi (i386_align_jumps_string); if (i386_align_jumps < 0 || i386_align_jumps > MAX_CODE_ALIGN) fatal ("-malign-jumps=%d is not between 0 and %d", i386_align_jumps, MAX_CODE_ALIGN); } else i386_align_jumps = def_align; /* Validate -malign-functions= value, or provide default */ if (i386_align_funcs_string) { i386_align_funcs = atoi (i386_align_funcs_string); if (i386_align_funcs < 0 || i386_align_funcs > MAX_CODE_ALIGN) fatal ("-malign-functions=%d is not between 0 and %d", i386_align_funcs, MAX_CODE_ALIGN); } else i386_align_funcs = def_align;}/* A C statement (sans semicolon) to choose the order in which to allocate hard registers for pseudo-registers local to a basic block. Store the desired register order in the array `reg_alloc_order'. Element 0 should be the register to allocate first; element 1, the next register; and so on. The macro body should not assume anything about the contents of `reg_alloc_order' before execution of the macro. On most machines, it is not necessary to define this macro. */voidorder_regs_for_local_alloc (){ int i, ch, order, regno; /* User specified the register allocation order */ if (i386_reg_alloc_order) { for (i = order = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++) { switch (ch) { case 'a': regno = 0; break; case 'd': regno = 1; break; case 'c': regno = 2; break; case 'b': regno = 3; break; case 'S': regno = 4; break; case 'D': regno = 5; break; case 'B': regno = 6; break; } reg_alloc_order[order++] = regno; } for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { if (!regs_allocated[i]) reg_alloc_order[order++] = i; } } /* If users did not specify a register allocation order, favor eax normally except if DImode variables are used, in which case favor edx before eax, which seems to cause less spill register not found messages. */ else { rtx insn; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) reg_alloc_order[i] = i; if (optimize) { int use_dca = FALSE; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == INSN) { rtx set = NULL_RTX; rtx pattern = PATTERN (insn); if (GET_CODE (pattern) == SET) set = pattern; else if ((GET_CODE (pattern) == PARALLEL || GET_CODE (pattern) == SEQUENCE) && GET_CODE (XVECEXP (pattern, 0, 0)) == SET) set = XVECEXP (pattern, 0, 0); if (set && GET_MODE (SET_SRC (set)) == DImode) { use_dca = TRUE; break; } } } if (use_dca) { reg_alloc_order[0] = 1; /* edx */ reg_alloc_order[1] = 2; /* ecx */ reg_alloc_order[2] = 0; /* eax */ } } }}/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific attribute for DECL. The attributes in ATTRIBUTES have previously been assigned to DECL. */inti386_valid_decl_attribute_p (decl, attributes, identifier, args) tree decl; tree attributes; tree identifier; tree args;{ return 0;}/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific attribute for TYPE. The attributes in ATTRIBUTES have previously been assigned to TYPE. */inti386_valid_type_attribute_p (type, attributes, identifier, args) tree type; tree attributes; tree identifier; tree args;{ if (TREE_CODE (type) != FUNCTION_TYPE && TREE_CODE (type) != FIELD_DECL && TREE_CODE (type) != TYPE_DECL) return 0; /* Stdcall attribute says callee is responsible for popping arguments if they are not variable. */ if (is_attribute_p ("stdcall", identifier)) return (args == NULL_TREE); /* Cdecl attribute says the callee is a normal C declaration */ if (is_attribute_p ("cdecl", identifier)) return (args == NULL_TREE); /* Regparm attribute specifies how many integer arguments are to be passed in registers */ if (is_attribute_p ("regparm", identifier)) { tree cst; if (!args || TREE_CODE (args) != TREE_LIST || TREE_CHAIN (args) != NULL_TREE || TREE_VALUE (args) == NULL_TREE) return 0; cst = TREE_VALUE (args); if (TREE_CODE (cst) != INTEGER_CST) return 0; if (TREE_INT_CST_HIGH (cst) != 0 || TREE_INT_CST_LOW (cst) < 0 || TREE_INT_CST_LOW (cst) > REGPARM_MAX) return 0; return 1; } return 0;}/* Return 0 if the attributes for two types are incompatible, 1 if they are compatible, and 2 if they are nearly compatible (which causes a warning to be generated). */inti386_comp_type_attributes (type1, type2) tree type1; tree type2;{ return 1;}/* Value is the number of bytes of arguments automatically popped when returning from a subroutine call. FUNDECL is the declaration node of the function (as a tree), FUNTYPE is the data type of the function (as a tree), or for a library call it is an identifier node for the subroutine name. SIZE is the number of bytes of arguments passed on the stack. On the 80386, the RTD insn may be used to pop them if the number of args is fixed, but if the number is variable then the caller must pop them all. RTD can't be used for library calls now because the library is compiled with the Unix compiler. Use of RTD is a selectable option, since it is incompatible with standard Unix calling sequences. If the option is not selected, the caller must always pop the args. The attribute stdcall is equivalent to RTD on a per module basis. */inti386_return_pops_args (fundecl, funtype, size) tree fundecl; tree funtype; int size;{ int rtd = TARGET_RTD; if (TREE_CODE (funtype) == IDENTIFIER_NODE) return 0; /* Cdecl functions override -mrtd, and never pop the stack */ if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) return 0; /* Stdcall functions will pop the stack if not variable args */ if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype))) rtd = 1; if (rtd) { if (TYPE_ARG_TYPES (funtype) == NULL_TREE || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node)) return size; if (aggregate_value_p (TREE_TYPE (funtype))) return GET_MODE_SIZE (Pmode); } return 0;}/* Argument support functions. *//* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a function whose data type is FNTYPE. For a library call, FNTYPE is 0. */voidinit_cumulative_args (cum, fntype, libname) CUMULATIVE_ARGS *cum; /* argument info to initialize */ tree fntype; /* tree ptr for function decl */ rtx libname; /* SYMBOL_REF of library name or 0 */{ static CUMULATIVE_ARGS zero_cum; tree param, next_param; if (TARGET_DEBUG_ARG) { fprintf (stderr, "\ninit_cumulative_args ("); if (fntype) { tree ret_type = TREE_TYPE (fntype); fprintf (stderr, "fntype code = %s, ret code = %s", tree_code_name[ (int)TREE_CODE (fntype) ], tree_code_name[ (int)TREE_CODE (ret_type) ]); } else fprintf (stderr, "no fntype"); if (libname) fprintf (stderr, ", libname = %s", XSTR (libname, 0)); } *cum = zero_cum; /* Set up the number of registers to use for passing arguments. */ cum->nregs = i386_regparm; if (fntype) { tree attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (fntype)); if (attr) cum->nregs = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))); } /* Determine if this function has variable arguments. This is indicated by the last argument being 'void_type_mode' if there are no variable arguments. If there are variable arguments, then we won't pass anything in registers */ if (cum->nregs) { for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; param != (tree)0; param = next_param) { next_param = TREE_CHAIN (param); if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node) cum->nregs = 0; } } if (TARGET_DEBUG_ARG) fprintf (stderr, ", nregs=%d )\n", cum->nregs); return;}/* Update the data in CUM to advance over an argument of mode MODE and data type TYPE. (TYPE is null for libcalls where that information may not be available.) */voidfunction_arg_advance (cum, mode, type, named) CUMULATIVE_ARGS *cum; /* current arg information */ enum machine_mode mode; /* current arg mode */ tree type; /* type of the argument or 0 if lib support */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -