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

📄 i386.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 4 页
字号:
/* Subroutines for insn-output.c for Intel 80386.   Copyright (C) 1988, 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.  */#include <stdio.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"#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, *i386_compare_op1;struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();/* Output an insn whose source is a 386 integer register.  SRC is the   rtx for the register, and TEMPLATE is the op-code template.  SRC may   be either SImode or DImode.   The template will be output with operands[0] as SRC, and operands[1]   as a pointer to the top of the 386 stack.  So a call from floatsidf2   would look like this:      output_op_from_reg (operands[1], AS1 (fild%z0,%1));   where %z0 corresponds to the caller's operands[1], and is used to   emit the proper size suffix.   ??? Extend this to handle HImode - a 387 can load and store HImode   values directly. */voidoutput_op_from_reg (src, template)     rtx src;     char *template;{  rtx xops[4];  xops[0] = src;  xops[1] = AT_SP (Pmode);  xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (src)));  xops[3] = stack_pointer_rtx;  if (GET_MODE_SIZE (GET_MODE (src)) > UNITS_PER_WORD)    {      rtx high = gen_rtx (REG, SImode, REGNO (src) + 1);      output_asm_insn (AS1 (push%L0,%0), &high);    }  output_asm_insn (AS1 (push%L0,%0), &src);  output_asm_insn (template, xops);  output_asm_insn (AS2 (add%L3,%2,%3), xops);}/* Output an insn to pop an value from the 387 top-of-stack to 386   register DEST. The 387 register stack is popped if DIES is true.  If   the mode of DEST is an integer mode, a `fist' integer store is done,   otherwise a `fst' float store is done. */voidoutput_to_reg (dest, dies)     rtx dest;     int dies;{  rtx xops[4];  xops[0] = AT_SP (Pmode);  xops[1] = stack_pointer_rtx;  xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (dest)));  xops[3] = dest;  output_asm_insn (AS2 (sub%L1,%2,%1), xops);  if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT)    {      if (dies)	output_asm_insn (AS1 (fistp%z3,%y0), xops);      else	output_asm_insn (AS1 (fist%z3,%y0), xops);    }  else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT)    {      if (dies)	output_asm_insn (AS1 (fstp%z3,%y0), xops);      else	output_asm_insn (AS1 (fst%z3,%y0), xops);    }  else    abort ();  output_asm_insn (AS1 (pop%L0,%0), &dest);  if (GET_MODE_SIZE (GET_MODE (dest)) > UNITS_PER_WORD)    {      dest = gen_rtx (REG, SImode, REGNO (dest) + 1);      output_asm_insn (AS1 (pop%L0,%0), &dest);    }}char *singlemove_string (operands)     rtx *operands;{  rtx x;  if (GET_CODE (operands[0]) == MEM      && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC)    {      if (XEXP (x, 0) != stack_pointer_rtx)	abort ();      return "push%L1 %1";    }  else if (GET_CODE (operands[1]) == CONST_DOUBLE)    {      return output_move_const_single (operands);    }  else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG)    return AS2 (mov%L0,%1,%0);  else if (CONSTANT_P (operands[1]))    return AS2 (mov%L0,%1,%0);  else    {      output_asm_insn ("push%L1 %1", operands);      return "pop%L0 %0";    }}/* Return a REG that occurs in ADDR with coefficient 1.   ADDR can be effectively incremented by incrementing REG.  */static rtxfind_addr_reg (addr)     rtx addr;{  while (GET_CODE (addr) == PLUS)    {      if (GET_CODE (XEXP (addr, 0)) == REG)	addr = XEXP (addr, 0);      else if (GET_CODE (XEXP (addr, 1)) == REG)	addr = XEXP (addr, 1);      else if (CONSTANT_P (XEXP (addr, 0)))	addr = XEXP (addr, 1);      else if (CONSTANT_P (XEXP (addr, 1)))	addr = XEXP (addr, 0);      else	abort ();    }  if (GET_CODE (addr) == REG)    return addr;  abort ();}/* Output an insn to add the constant N to the register X.  */static voidasm_add (n, x)     int n;     rtx x;{  rtx xops[2];  xops[1] = x;  if (n < 0)    {      xops[0] = GEN_INT (-n);      output_asm_insn (AS2 (sub%L0,%0,%1), xops);    }  else if (n > 0)    {      xops[0] = GEN_INT (n);      output_asm_insn (AS2 (add%L0,%0,%1), xops);    }}/* Output assembler code to perform a doubleword move insn   with operands OPERANDS.  */char *output_move_double (operands)     rtx *operands;{  enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;  rtx latehalf[2];  rtx addreg0 = 0, addreg1 = 0;  int dest_overlapped_low = 0;  /* 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)) == POST_INC)    optype0 = POPOP;  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)    optype0 = PUSHOP;  else if (GET_CODE (operands[0]) == MEM)    optype0 = MEMOP;  else    optype0 = RNDOP;  if (REG_P (operands[1]))    optype1 = REGOP;  else if (CONSTANT_P (operands[1]))    optype1 = CNSTOP;  else if (offsettable_memref_p (operands[1]))    optype1 = OFFSOP;  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)    optype1 = POPOP;  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)    optype1 = PUSHOP;  else if (GET_CODE (operands[1]) == MEM)    optype1 = MEMOP;  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 ();  /* If one operand is decrementing and one is incrementing     decrement the former register explicitly     and change that operand into ordinary indexing.  */  if (optype0 == PUSHOP && optype1 == POPOP)    {      operands[0] = XEXP (XEXP (operands[0], 0), 0);      asm_add (-8, operands[0]);      operands[0] = gen_rtx (MEM, DImode, operands[0]);      optype0 = OFFSOP;    }  if (optype0 == POPOP && optype1 == PUSHOP)    {      operands[1] = XEXP (XEXP (operands[1], 0), 0);      asm_add (-8, operands[1]);      operands[1] = gen_rtx (MEM, DImode, operands[1]);      optype1 = OFFSOP;    }  /* If an operand is an unoffsettable memory ref, find a register     we can increment temporarily to make it refer to the second word.  */  if (optype0 == MEMOP)    addreg0 = find_addr_reg (XEXP (operands[0], 0));  if (optype1 == MEMOP)    addreg1 = find_addr_reg (XEXP (operands[1], 0));  /* 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)    {      if (GET_CODE (operands[1]) == CONST_DOUBLE)	split_double (operands[1], &operands[1], &latehalf[1]);      else if (CONSTANT_P (operands[1]))	{	  if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0)	    latehalf[1] = constm1_rtx;	  else	    latehalf[1] = const0_rtx;	}    }  else    latehalf[1] = operands[1];  /* If insn is effectively movd N (sp),-(sp) 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.  */  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];  /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)),     if the upper part of reg N does not appear in the MEM, arrange to     emit the move late-half first.  Otherwise, compute the MEM address     into the upper part of N and use that as a pointer to the memory     operand.  */  if (optype0 == REGOP      && (optype1 == OFFSOP || optype1 == MEMOP))    {      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,	     compute the address into latehalf of dest.  */	  rtx xops[2];	  xops[0] = latehalf[0];	  xops[1] = XEXP (operands[1], 0);	  output_asm_insn (AS2 (lea%L0,%a1,%0), xops);	  operands[1] = gen_rtx (MEM, DImode, latehalf[0]);	  latehalf[1] = adj_offsettable_operand (operands[1], 4);	}      else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))	/* If the low half of dest is mentioned in the source memory	   address, the arrange to emit the move late half first.  */	dest_overlapped_low = 1;    }  /* If one or both operands autodecrementing,     do the two words, high-numbered first.  */  /* Likewise,  the first move would clobber the source of the second one,     do them in the other order.  This happens only for registers;     such overlap can't happen in memory unless the user explicitly     sets it up, and that is an undefined circumstance.  */  if (optype0 == PUSHOP || optype1 == PUSHOP      || (optype0 == REGOP && optype1 == REGOP	  && REGNO (operands[0]) == REGNO (latehalf[1]))      || dest_overlapped_low)    {      /* Make any unoffsettable addresses point at high-numbered word.  */      if (addreg0)	asm_add (4, addreg0);      if (addreg1)	asm_add (4, addreg1);      /* Do that word.  */      output_asm_insn (singlemove_string (latehalf), latehalf);      /* Undo the adds we just did.  */      if (addreg0)         asm_add (-4, addreg0);      if (addreg1)	asm_add (-4, addreg1);      /* Do low-numbered word.  */      return singlemove_string (operands);    }  /* Normal case: do the two words, low-numbered first.  */  output_asm_insn (singlemove_string (operands), operands);  /* Make any unoffsettable addresses point at high-numbered word.  */  if (addreg0)    asm_add (4, addreg0);  if (addreg1)    asm_add (4, addreg1);  /* Do that word.  */  output_asm_insn (singlemove_string (latehalf), latehalf);  /* Undo the adds we just did.  */  if (addreg0)    asm_add (-4, addreg0);  if (addreg1)    asm_add (-4, addreg1);  return "";}intstandard_80387_constant_p (x)     rtx x;{  union real_extract u;  register double d;  bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u);  d = u.d;  if (d == 0)    return 1;  if (d == 1)    return 2;  /* Note that on the 80387, other constants, such as pi,     are much slower to load as standard constants     than to load from doubles in memory!  */  return 0;}char *

⌨️ 快捷键说明

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