⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reg-stack.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 5 页
字号:
/* Register to Stack convert for GNU compiler.   Copyright (C) 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA.  *//* This pass converts stack-like registers from the "flat register   file" model that gcc uses, to a stack convention that the 387 uses.   * The form of the input:   On input, the function consists of insn that have had their   registers fully allocated to a set of "virtual" registers.  Note that   the word "virtual" is used differently here than elsewhere in gcc: for   each virtual stack reg, there is a hard reg, but the mapping between   them is not known until this pass is run.  On output, hard register   numbers have been substituted, and various pop and exchange insns have   been emitted.  The hard register numbers and the virtual register   numbers completely overlap - before this pass, all stack register   numbers are virtual, and afterward they are all hard.   The virtual registers can be manipulated normally by gcc, and their   semantics are the same as for normal registers.  After the hard   register numbers are substituted, the semantics of an insn containing   stack-like regs are not the same as for an insn with normal regs: for   instance, it is not safe to delete an insn that appears to be a no-op   move.  In general, no insn containing hard regs should be changed   after this pass is done.   * The form of the output:   After this pass, hard register numbers represent the distance from   the current top of stack to the desired register.  A reference to   FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1,   represents the register just below that, and so forth.  Also, REG_DEAD   notes indicate whether or not a stack register should be popped.   A "swap" insn looks like a parallel of two patterns, where each   pattern is a SET: one sets A to B, the other B to A.   A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG   and whose SET_DEST is REG or MEM.  Any other SET_DEST, such as PLUS,   will replace the existing stack top, not push a new value.   A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose   SET_SRC is REG or MEM.   The case where the SET_SRC and SET_DEST are both FIRST_STACK_REG   appears ambiguous.  As a special case, the presence of a REG_DEAD note   for FIRST_STACK_REG differentiates between a load insn and a pop.   If a REG_DEAD is present, the insn represents a "pop" that discards   the top of the register stack.  If there is no REG_DEAD note, then the   insn represents a "dup" or a push of the current top of stack onto the   stack.   * Methodology:   Existing REG_DEAD and REG_UNUSED notes for stack registers are   deleted and recreated from scratch.  REG_DEAD is never created for a   SET_DEST, only REG_UNUSED.   Before life analysis, the mode of each insn is set based on whether   or not any stack registers are mentioned within that insn.  VOIDmode   means that no regs are mentioned anyway, and QImode means that at   least one pattern within the insn mentions stack registers.  This   information is valid until after reg_to_stack returns, and is used   from jump_optimize.   * asm_operands:   There are several rules on the usage of stack-like regs in   asm_operands insns.  These rules apply only to the operands that are   stack-like regs:   1. Given a set of input regs that die in an asm_operands, it is      necessary to know which are implicitly popped by the asm, and      which must be explicitly popped by gcc.	An input reg that is implicitly popped by the asm must be	explicitly clobbered, unless it is constrained to match an	output operand.   2. For any input reg that is implicitly popped by an asm, it is      necessary to know how to adjust the stack to compensate for the pop.      If any non-popped input is closer to the top of the reg-stack than      the implicitly popped reg, it would not be possible to know what the      stack looked like - it's not clear how the rest of the stack "slides      up".	All implicitly popped input regs must be closer to the top of	the reg-stack than any input that is not implicitly popped.   3. It is possible that if an input dies in an insn, reload might      use the input reg for an output reload.  Consider this example:		asm ("foo" : "=t" (a) : "f" (b));      This asm says that input B is not popped by the asm, and that      the asm pushes a result onto the reg-stack, ie, the stack is one      deeper after the asm than it was before.  But, it is possible that      reload will think that it can use the same reg for both the input and      the output, if input B dies in this insn.	If any input operand uses the "f" constraint, all output reg	constraints must use the "&" earlyclobber.      The asm above would be written as		asm ("foo" : "=&t" (a) : "f" (b));   4. Some operands need to be in particular places on the stack.  All      output operands fall in this category - there is no other way to      know which regs the outputs appear in unless the user indicates      this in the constraints.	Output operands must specifically indicate which reg an output	appears in after an asm.  "=f" is not allowed: the operand	constraints must select a class with a single reg.   5. Output operands may not be "inserted" between existing stack regs.      Since no 387 opcode uses a read/write operand, all output operands      are dead before the asm_operands, and are pushed by the asm_operands.      It makes no sense to push anywhere but the top of the reg-stack.	Output operands must start at the top of the reg-stack: output	operands may not "skip" a reg.   6. Some asm statements may need extra stack space for internal      calculations.  This can be guaranteed by clobbering stack registers      unrelated to the inputs and outputs.   Here are a couple of reasonable asms to want to write.  This asm   takes one input, which is internally popped, and produces two outputs.	asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp));   This asm takes two inputs, which are popped by the fyl2xp1 opcode,   and replaces them with one output.  The user must code the "st(1)"   clobber for reg-stack.c to know that fyl2xp1 pops both inputs.	asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)");   */#include <stdio.h>#include "config.h"#include "tree.h"#include "rtl.h"#include "insn-config.h"#include "regs.h"#include "hard-reg-set.h"#include "flags.h"#ifdef STACK_REGS#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)/* True if the current function returns a real value. */static int current_function_returns_real;/* This is the basic stack record.  TOP is an index into REG[] such   that REG[TOP] is the top of stack.  If TOP is -1 the stack is empty.   If TOP is -2 the stack is not yet initialized: reg_set indicates   which registers are live.  Stack initialization consists of placing   each live reg in array `reg' and setting `top' appropriately. */typedef struct stack_def{  int top;			/* index to top stack element */  HARD_REG_SET reg_set;		/* set of live registers */  char reg[REG_STACK_SIZE];	/* register - stack mapping */} *stack;/* highest instruction uid */static int max_uid = 0;/* Number of basic blocks in the current function.  */static int blocks;/* Element N is first insn in basic block N.   This info lasts until we finish compiling the function.  */static rtx *block_begin;/* Element N is last insn in basic block N.   This info lasts until we finish compiling the function.  */static rtx *block_end;/* Element N is nonzero if control can drop into basic block N */static char *block_drops_in;/* Element N says all about the stack at entry block N */static stack block_stack_in;/* Element N says all about the stack life at the end of block N */static HARD_REG_SET *block_out_reg_set;/* This is where the BLOCK_NUM values are really stored.  This is set   up by find_blocks and used there and in life_analysis.  It can be used   later, but only to look up an insn that is the head or tail of some   block.  life_analysis and the stack register conversion process can   add insns within a block. */static short *block_number;/* This is the register file for all register after conversion */static rtx FP_mode_reg[FIRST_PSEUDO_REGISTER][(int) MAX_MACHINE_MODE];/* Get the basic block number of an insn.  See note at block_number   definition are validity of this information. */#define BLOCK_NUM(INSN)  \  (((INSN_UID (INSN) > max_uid)	\    ? (short *)(abort() , 0)		\    : block_number)[INSN_UID (INSN)])extern rtx gen_jump ();extern rtx gen_movdf ();extern rtx find_regno_note ();extern rtx emit_jump_insn_before ();extern rtx emit_label_after ();/* Forward declarations */static void find_blocks ();static void stack_reg_life_analysis ();static void change_stack ();static void convert_regs ();static void dump_stack_info ();/* Return non-zero if any stack register is mentioned somewhere within PAT.  */intstack_regs_mentioned_p (pat)     rtx pat;{  register char *fmt;  register int i;  if (STACK_REG_P (pat))    return 1;  fmt = GET_RTX_FORMAT (GET_CODE (pat));  for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)    {      if (fmt[i] == 'E')	{	  register int j;	  for (j = XVECLEN (pat, i) - 1; j >= 0; j--)	    if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))	      return 1;	}      else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i)))	return 1;    }  return 0;}/* Convert register usage from "flat" register file usage to a "stack   register file.  FIRST is the first insn in the function, FILE is the   dump file, if used.   First compute the beginning and end of each basic block.  Do a   register life analysis on the stack registers, recording the result   for the head and tail of each basic block.  The convert each insn one   by one.  Run a last jump_optimize() pass, if optimizing, to eliminate   any cross-jumping created when the converter inserts pop insns.*/voidreg_to_stack (first, file)     rtx first;     FILE *file;{  register rtx insn;  register int i;  int stack_reg_seen = 0;  enum machine_mode mode;  current_function_returns_real    = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE;  for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;       mode = GET_MODE_WIDER_MODE (mode))    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)      FP_mode_reg[i][(int) mode] = gen_rtx (REG, mode, i);  /* Count the basic blocks.  Also find maximum insn uid.  */  {    register RTX_CODE prev_code = JUMP_INSN;    register RTX_CODE code;    max_uid = 0;    blocks = 0;    for (insn = first; insn; insn = NEXT_INSN (insn))      {	/* Note that this loop must select the same block boundaries	   as code in find_blocks. */	if (INSN_UID (insn) > max_uid)	  max_uid = INSN_UID (insn);	code = GET_CODE (insn);	if (code == CODE_LABEL	    || (prev_code != INSN		&& prev_code != CALL_INSN		&& prev_code != CODE_LABEL		&& (code == INSN || code == CALL_INSN || code == JUMP_INSN)))	  blocks++;	/* Remember whether or not this insn mentions an FP regs.	   Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */	if ((GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN	     || GET_CODE (insn) == JUMP_INSN)	    && stack_regs_mentioned_p (PATTERN (insn)))	  {	    stack_reg_seen = 1;	    PUT_MODE (insn, QImode);	  }	else	  PUT_MODE (insn, VOIDmode);	if (code != NOTE)	  prev_code = code;      }  }  /* If no stack register reference exists in this insn, there isn't     anything to convert.  */  if (! stack_reg_seen)    return;  /* If there are stack registers, there must be at least one block. */  if (! blocks)    abort ();  /* Allocate some tables that last till end of compiling this function     and some needed only in find_blocks and life_analysis. */  block_begin = (rtx *) alloca (blocks * sizeof (rtx));  block_end = (rtx *) alloca (blocks * sizeof (rtx));  block_drops_in = (char *) alloca (blocks);  block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def));  block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET));  bzero (block_stack_in, blocks * sizeof (struct stack_def));  bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET));  block_number = (short *) alloca ((max_uid + 1) * sizeof (short));  find_blocks (first);  stack_reg_life_analysis (first);  /* Dump the life analysis debug information before jump     optimization, as that will destroy the LABEL_REFS we keep the     information in. */  if (file)    dump_stack_info (file);  convert_regs ();  if (optimize)    jump_optimize (first, 2, 0, 0);}/* Check PAT, which is in INSN, for LABEL_REFs.  Add INSN to the   label's chain of references, and note which insn contains each   reference. */static voidrecord_label_references (insn, pat)     rtx insn, pat;{  register enum rtx_code code = GET_CODE (pat);  register int i;  register char *fmt;  if (code == LABEL_REF)    {      register rtx label = XEXP (pat, 0);      register rtx ref;      if (GET_CODE (label) != CODE_LABEL)	abort ();      /* Don't make a duplicate in the code_label's chain. */      for (ref = LABEL_REFS (label); ref != label; ref = LABEL_NEXTREF (ref))	if (CONTAINING_INSN (ref) == insn)	  return;      CONTAINING_INSN (pat) = insn;      LABEL_NEXTREF (pat) = LABEL_REFS (label);      LABEL_REFS (label) = pat;      return;    }  fmt = GET_RTX_FORMAT (code);  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)    {      if (fmt[i] == 'e')	record_label_references (insn, XEXP (pat, i));      if (fmt[i] == 'E')	{	  register int j;	  for (j = 0; j < XVECLEN (pat, i); j++)	    record_label_references (insn, XVECEXP (pat, i, j));	}    }}/* Return a pointer to the REG expression within PAT.  If PAT is not a   REG, possible enclosed by a conversion rtx, return the inner part of   PAT that stopped the search. */static rtx *get_true_reg (pat)     rtx *pat;{  while (GET_CODE (*pat) == SUBREG	 || GET_CODE (*pat) == FLOAT	 || GET_CODE (*pat) == FIX	 || GET_CODE (*pat) == FLOAT_EXTEND	 || GET_CODE (*pat) == FLOAT_TRUNCATE)    pat = & XEXP (*pat, 0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -