📄 out-i386.c
字号:
/* Subroutines for insn-output.c for Intel 80386. Copyright (C) 1988 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 1, 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. */#ifndef FILE#include <stdio.h>#endif#define FP_TOP (gen_rtx(REG, DFmode, FIRST_FLOAT_REG))#define AT_SP(mode) (gen_rtx (MEM, (mode), stack_pointer_rtx))#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))#define RET return ""/* #define RETCOM(X) fprintf (asm_out_file, "%sX fp_pop_level=%d\n", \ COMMENT_BEGIN, fp_pop_level); RET */#define RETCOM(X) return ""#define POP_ONE_FP \ { /* fp_pop_level--; */ \ fprintf (asm_out_file, "\tfstp %sst (0)\n", RP); }extern FILE *asm_out_file;static char *singlemove_string ();static void output_movf ();static void replace_float_constant ();static int mentions_fp_top ();static int call_top_dead_p ();static int fp_top_dead_p1 ();static rtx via_memory ();static void output_asm_insn_double_reg_op ();/* All output functions must increment or decrement this to indicate the net number of pops or pushes which they perform. Note that it won't necessarily balance with the optimize running, since we might have two different calls with the same pop shared by cross jumping. However on optimize the reg dead heuristic seems to work. */int fp_pop_level = 0;static char *hi_reg_name[] = HI_REGISTER_NAMES;static char *qi_reg_name[] = QI_REGISTER_NAMES;/* for fabs, fch, .. where the argument operand[1] must first be moved to constraints "=fm" "0" */#define FP_CALL1(op) \ { if (FP_REG_P (operands[0])) \ return op; \ output_movf (FP_TOP, operands[1]); \ output_asm_insn (op, operands); \ /* fp_pop_level--; */ \ return "fstp%z0 %0"; }/* handle case of call where op0/op1 is "=mf" and opn is "mrf" eg. fadd */#define FP_CALL(op, rev, n) \ return fp_call_internal (op, rev, n, operands, insn);static char *fp_call_internal (op, rev, n, operands, insn) char *op; char *rev; int n; rtx *operands; rtx insn;{ if (!FP_REG_P (operands[0])) { /* Here destination is in memory and source is in the fp stack. */ output_movf (FP_TOP, operands[0]); output_asm_insn_double_reg_op (op, rev, insn); return "fstp%z0 %0"; } if (FP_REG_P (operands[n])) { rtx temp = operands[1]; char *tem1 = op; operands[1] = operands[n]; op = rev; operands[n] = temp; rev = tem1; } if (REG_P (operands[n])) { rtx xops[2]; via_memory (operands[n]); operands[n] = AT_SP (GET_MODE (operands[n])); xops[0] = stack_pointer_rtx; xops[1] = gen_rtx (CONST_INT, VOIDmode, GET_MODE_SIZE (GET_MODE (operands[n]))); output_asm_insn (op, operands + n); output_asm_insn (AS2 (add%L0,%1,%0), xops); } else output_asm_insn (op, operands + n); if (FP_REG_P (operands[0])) { /* It turns out not to work to use top_dead_p because the death notes are not accurate enough. But this ought to work, because the only thing that can live across basic blocks is reg 8, and these insns never involve reg 8 directly. */ if (fp_top_dead_p1 (insn)) POP_ONE_FP; } RET;}/* Output assembler code to perform insn OP with two stack operands, and output on the stack. REV is the assembler insn that does the same thing but effectively interchanges the meanings of the two arguments. Somewhat counterintuitively, the "first" operand was pushed last. The output replaces either the top-of-stack or both of the arguments, depending on whether the other argument is wanted after this insn. */static voidoutput_asm_insn_double_reg_op (op, rev, insn) char *op; char *rev; rtx insn;{ fputc ('\t', asm_out_file); if (top_dead_p (insn)) { /* Here we want the "reversed" insn, fsubr or fdivr. But there is an assembler bug in all 80386 assemblers which exchanges the meanings of fsubr and fsub, and of fdivr and fdiv! So use the "unreversed" opcode (which will assemble into the "reversed" insn). */ rev = op; while (*rev && *rev != '%') fputc (*rev++, asm_out_file); /* fp_pop_level--; */ fprintf (asm_out_file, AS2 (p,%sst,%sst(1)), RP, RP); } else { while (*op && *op != '%') fputc (*op++, asm_out_file); fprintf (asm_out_file,AS2 ( ,%sst(1),%sst), RP, RP); } putc ('\n', asm_out_file);}/* Moves X to memory location 8 below stack pointer and returns an RTX for that memory location. X should be a register, in DFmode or SFmode. */static rtxvia_memory (x) rtx x;{ if (!REG_P (x)) abort (); if (GET_MODE (x) == DFmode) { rtx xops[1]; xops[0] = gen_rtx (REG, SImode, REGNO (x) + 1); output_asm_insn ("push%L0 %0", xops); } output_asm_insn ("push%L0 %0", &x);}/* Output an insn to copy the SFmode value in fp0 to OPERAND without clobbering fp0. */voidfp_store_sf (target) rtx target;{ if (REG_P (target)) { rtx xoperands[3]; xoperands[0] = stack_pointer_rtx; xoperands[1] = AT_SP (Pmode); xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4); output_asm_insn (AS2 (add%L0,%2,%0), xoperands); output_asm_insn ("fst%S0 %1", xoperands); output_asm_insn ("pop%L0 %0", &target); } else if (GET_CODE (target) == MEM) output_asm_insn ("fst%S0 %0", &target);}/* Output an insn to pop an SF value from fp0 into TARGET. This destroys the value of fp0. */voidfp_pop_sf (target) rtx target;{ if (REG_P (target)) { rtx xoperands[3]; xoperands[0] = stack_pointer_rtx; xoperands[1] = AT_SP (Pmode); xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4); output_asm_insn (AS2 (add%L0,%2,%0), xoperands); output_asm_insn ("fstp%S0 %1", xoperands); output_asm_insn ("pop%L0 %0", &target); /* fp_pop_level--; */ } else if (GET_CODE (target) == MEM) { /* fp_pop_level--; */ output_asm_insn ("fstp%S0 %0", &target); } else abort ();}/* Copy the top of the fpu stack into TARGET, without popping. */voidfp_store_df (target) rtx target;{ if (REG_P (target)) { rtx xoperands[4]; xoperands[0] = stack_pointer_rtx; xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1); xoperands[2] = AT_SP (Pmode); xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8); output_asm_insn (AS2 (add%L0,%3,%0), xoperands); output_asm_insn ("fst%Q0 %2", xoperands); output_asm_insn ("pop%L0 %0", &target); output_asm_insn ("pop%L0 %1", xoperands); } else if (GET_CODE (target) == MEM) output_asm_insn ("fst%Q0 %0", &target);}/* Copy the top of the fpu stack into TARGET, with popping. */voidfp_pop_df (target) rtx target;{ if (REG_P (target)) { rtx xoperands[4]; xoperands[0] = stack_pointer_rtx; xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1); xoperands[2] = AT_SP (Pmode); xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8); output_asm_insn (AS2 (add%L0,%3,%0), xoperands); /* fp_pop_level--; */ output_asm_insn ("fstp%Q0 %2", xoperands); output_asm_insn ("pop%L0 %0", &target); output_asm_insn ("pop%L0 %1", xoperands); } else if (GET_CODE (target) == MEM) { /* fp_pop_level--; */ output_asm_insn ("fstp%z0 %0", &target); }}#if 0/* Pop the fp stack, convert value to integer and store in TARGET. TARGET may be memory or register, and may have QI, HI or SImode. */voidfp_pop_int (target) rtx target;{ if (REG_P (target) || GET_MODE (target) != SImode) { rtx xxops[2]; xxops[0] = stack_pointer_rtx; xxops[1] = gen_rtx (CONST_INT, VOIDmode, 4); output_asm_insn (AS2 (sub%L0,%1,%0), xxops); xxops[0] = AT_SP (Pmode); /* fp_pop_level--; */ output_asm_insn ("fistp%L0 %0", xxops); output_asm_insn ("pop%L0 %0", &target); } else if (GET_CODE (target) == MEM) { /* fp_pop_level--; */ output_asm_insn ("fistp%L0 %0", &target); } else abort ();}#endif/* Push the SFmode value X onto the fpu stack. */voidfp_push_sf (x) rtx x;{ /* fp_pop_level++; */ if (REG_P (x)) { rtx xoperands[2]; rtx xfops[3]; output_asm_insn ("push%L0 %0", &x); xfops[0] = AT_SP (Pmode); xfops[2] = gen_rtx (CONST_INT, VOIDmode, 4); xfops[1] = stack_pointer_rtx; output_asm_insn ("fld%S0 %0 \n\tadd%L0 %2,%1", xfops); } else output_asm_insn ("fld%S0 %0", &x);}/* Push the DFmode value X onto the fpu stack. */voidfp_push_df (x) rtx x;{ /* fp_pop_level++; */ if (REG_P (x)) { rtx xoperands[2]; rtx xfops[3]; xoperands[0] = x; xoperands[1] = gen_rtx (REG, SImode, REGNO (x) + 1); output_asm_insn ("push%L0 %1", xoperands); output_asm_insn ("push%L0 %0", xoperands); xfops[0] = AT_SP (Pmode); xfops[2] = gen_rtx (CONST_INT, VOIDmode, 8); xfops[1] = stack_pointer_rtx; output_asm_insn ("fld%Q0 %0 \n\tadd%L0 %2,%1", xfops); } else if (GET_CODE (x) == MEM) output_asm_insn ("fld%Q0 %0", &x);}static char *output_move_const_single ();static 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%L0 %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%L0 %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_rtx (CONST_INT, VOIDmode, -n); output_asm_insn (AS2 (sub%L0,%0,%1), xops); } else if (n > 0) { xops[0] = gen_rtx (CONST_INT, VOIDmode, 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; /* 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]) || GET_CODE (operands[1]) == CONST_DOUBLE) 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -