📄 i386.c
字号:
/* 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 + -