📄 stormy16.c
字号:
/* Xstormy16 target functions. Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Red Hat, 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 "config.h"#include "system.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 "flags.h"#include "recog.h"#include "toplev.h"#include "obstack.h"#include "tree.h"#include "expr.h"#include "optabs.h"#include "output.h"#include "except.h"#include "function.h"#include "target.h"#include "target-def.h"#include "tm_p.h"#include "langhooks.h"static rtx emit_addhi3_postreload PARAMS ((rtx, rtx, rtx));static void xstormy16_asm_out_constructor PARAMS ((rtx, int));static void xstormy16_asm_out_destructor PARAMS ((rtx, int));static void xstormy16_encode_section_info PARAMS ((tree, int));static void xstormy16_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree));static void xstormy16_init_builtins PARAMS ((void));static rtx xstormy16_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));/* Define the information needed to generate branch and scc insns. This is stored from the compare operation. */struct rtx_def * xstormy16_compare_op0;struct rtx_def * xstormy16_compare_op1;/* Return 1 if this is a LT, GE, LTU, or GEU operator. */intxstormy16_ineqsi_operator (op, mode) register rtx op; enum machine_mode mode;{ enum rtx_code code = GET_CODE (op); return ((mode == VOIDmode || GET_MODE (op) == mode) && (code == LT || code == GE || code == LTU || code == GEU));}/* Return 1 if this is an EQ or NE operator. */intequality_operator (op, mode) register rtx op; enum machine_mode mode;{ return ((mode == VOIDmode || GET_MODE (op) == mode) && (GET_CODE (op) == EQ || GET_CODE (op) == NE));}/* Return 1 if this is a comparison operator but not an EQ or NE operator. */intinequality_operator (op, mode) register rtx op; enum machine_mode mode;{ return comparison_operator (op, mode) && ! equality_operator (op, mode);}/* Branches are handled as follows: 1. HImode compare-and-branches. The machine supports these natively, so the appropriate pattern is emitted directly. 2. SImode EQ and NE. These are emitted as pairs of HImode compare-and-branches. 3. SImode LT, GE, LTU and GEU. These are emitted as a sequence of a SImode subtract followed by a branch (not a compare-and-branch), like this: sub sbc blt 4. SImode GT, LE, GTU, LEU. These are emitted as a sequence like: sub sbc blt or bne*//* Emit a branch of kind CODE to location LOC. */voidxstormy16_emit_cbranch (code, loc) enum rtx_code code; rtx loc;{ rtx op0 = xstormy16_compare_op0; rtx op1 = xstormy16_compare_op1; rtx condition_rtx, loc_ref, branch, cy_clobber; rtvec vec; enum machine_mode mode; mode = GET_MODE (op0); if (mode != HImode && mode != SImode) abort (); if (mode == SImode && (code == GT || code == LE || code == GTU || code == LEU)) { int unsigned_p = (code == GTU || code == LEU); int gt_p = (code == GT || code == GTU); rtx lab = NULL_RTX; if (gt_p) lab = gen_label_rtx (); xstormy16_emit_cbranch (unsigned_p ? LTU : LT, gt_p ? lab : loc); /* This should be generated as a comparison against the temporary created by the previous insn, but reload can't handle that. */ xstormy16_emit_cbranch (gt_p ? NE : EQ, loc); if (gt_p) emit_label (lab); return; } else if (mode == SImode && (code == NE || code == EQ) && op1 != const0_rtx) { rtx lab = NULL_RTX; int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; int i; if (code == EQ) lab = gen_label_rtx (); for (i = 0; i < num_words - 1; i++) { xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, i * UNITS_PER_WORD); xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, i * UNITS_PER_WORD); xstormy16_emit_cbranch (NE, code == EQ ? lab : loc); } xstormy16_compare_op0 = simplify_gen_subreg (word_mode, op0, mode, i * UNITS_PER_WORD); xstormy16_compare_op1 = simplify_gen_subreg (word_mode, op1, mode, i * UNITS_PER_WORD); xstormy16_emit_cbranch (code, loc); if (code == EQ) emit_label (lab); return; } /* We can't allow reload to try to generate any reload after a branch, so when some register must match we must make the temporary ourselves. */ if (mode != HImode) { rtx tmp; tmp = gen_reg_rtx (mode); emit_move_insn (tmp, op0); op0 = tmp; } condition_rtx = gen_rtx (code, mode, op0, op1); loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc); branch = gen_rtx_SET (VOIDmode, pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx, loc_ref, pc_rtx)); cy_clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (BImode)); if (mode == HImode) vec = gen_rtvec (2, branch, cy_clobber); else if (code == NE || code == EQ) vec = gen_rtvec (2, branch, gen_rtx_CLOBBER (VOIDmode, op0)); else { rtx sub;#if 0 sub = gen_rtx_SET (VOIDmode, op0, gen_rtx_MINUS (SImode, op0, op1));#else sub = gen_rtx_CLOBBER (SImode, op0);#endif vec = gen_rtvec (3, branch, sub, cy_clobber); } emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));}/* Take a SImode conditional branch, one of GT/LE/GTU/LEU, and split the arithmetic operation. Most of the work is done by xstormy16_expand_arith. */voidxstormy16_split_cbranch (mode, label, comparison, dest, carry) enum machine_mode mode; rtx label; rtx comparison; rtx dest; rtx carry;{ rtx op0 = XEXP (comparison, 0); rtx op1 = XEXP (comparison, 1); rtx seq, last_insn; rtx compare; start_sequence (); xstormy16_expand_arith (mode, COMPARE, dest, op0, op1, carry); seq = get_insns (); end_sequence (); if (! INSN_P (seq)) abort (); last_insn = seq; while (NEXT_INSN (last_insn) != NULL_RTX) last_insn = NEXT_INSN (last_insn); compare = SET_SRC (XVECEXP (PATTERN (last_insn), 0, 0)); PUT_CODE (XEXP (compare, 0), GET_CODE (comparison)); XEXP (compare, 1) = gen_rtx_LABEL_REF (VOIDmode, label); emit_insn (seq);}/* Return the string to output a conditional branch to LABEL, which is the operand number of the label. OP is the conditional expression, or NULL for branch-always. REVERSED is nonzero if we should reverse the sense of the comparison. INSN is the insn. */char *xstormy16_output_cbranch_hi (op, label, reversed, insn) rtx op; const char * label; int reversed; rtx insn;{ static char string[64]; int need_longbranch = (op != NULL_RTX ? get_attr_length (insn) == 8 : get_attr_length (insn) == 4); int really_reversed = reversed ^ need_longbranch; const char *ccode; const char *template; const char *operands; enum rtx_code code; if (! op) { if (need_longbranch) ccode = "jmpf"; else ccode = "br"; sprintf (string, "%s %s", ccode, label); return string; } code = GET_CODE (op); if (GET_CODE (XEXP (op, 0)) != REG) { code = swap_condition (code); operands = "%3,%2"; } else operands = "%2,%3"; /* Work out which way this really branches. */ if (really_reversed) code = reverse_condition (code); switch (code) { case EQ: ccode = "z"; break; case NE: ccode = "nz"; break; case GE: ccode = "ge"; break; case LT: ccode = "lt"; break; case GT: ccode = "gt"; break; case LE: ccode = "le"; break; case GEU: ccode = "nc"; break; case LTU: ccode = "c"; break; case GTU: ccode = "hi"; break; case LEU: ccode = "ls"; break; default: abort (); } if (need_longbranch) template = "b%s %s,.+8 | jmpf %s"; else template = "b%s %s,%s"; sprintf (string, template, ccode, operands, label); return string;}/* Return the string to output a conditional branch to LABEL, which is the operand number of the label, but suitable for the tail of a SImode branch. OP is the conditional expression (OP is never NULL_RTX). REVERSED is nonzero if we should reverse the sense of the comparison. INSN is the insn. */char *xstormy16_output_cbranch_si (op, label, reversed, insn) rtx op; const char * label; int reversed; rtx insn;{ static char string[64]; int need_longbranch = get_attr_length (insn) >= 8; int really_reversed = reversed ^ need_longbranch; const char *ccode; const char *template; char prevop[16]; enum rtx_code code; code = GET_CODE (op); /* Work out which way this really branches. */ if (really_reversed) code = reverse_condition (code); switch (code) { case EQ: ccode = "z"; break; case NE: ccode = "nz"; break; case GE: ccode = "ge"; break; case LT: ccode = "lt"; break; case GEU: ccode = "nc"; break; case LTU: ccode = "c"; break; /* The missing codes above should never be generated. */ default: abort (); } switch (code) { case EQ: case NE: { int regnum; if (GET_CODE (XEXP (op, 0)) != REG) abort (); regnum = REGNO (XEXP (op, 0)); sprintf (prevop, "or %s,%s", reg_names[regnum], reg_names[regnum+1]); } break; case GE: case LT: case GEU: case LTU: strcpy (prevop, "sbc %2,%3"); break; default: abort (); } if (need_longbranch) template = "%s | b%s .+6 | jmpf %s"; else template = "%s | b%s %s"; sprintf (string, template, prevop, ccode, label); return string;}/* Many machines have some registers that cannot be copied directly to or from memory or even from other types of registers. An example is the `MQ' register, which on most machines, can only be copied to or from general registers, but not memory. Some machines allow copying all registers to and from memory, but require a scratch register for stores to some memory locations (e.g., those with symbolic address on the RT, and those with certain symbolic address on the SPARC when compiling PIC). In some cases, both an intermediate and a scratch register are required. You should define these macros to indicate to the reload phase that it may need to allocate at least one register for a reload in addition to the register to contain the data. Specifically, if copying X to a register CLASS in MODE requires an intermediate register, you should define `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of whose registers can be used as intermediate registers or scratch registers. If copying a register CLASS in MODE to X requires an intermediate or scratch register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the largest register class required. If the requirements for input and output reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used instead of defining both macros identically. The values returned by these macros are often `GENERAL_REGS'. Return `NO_REGS' if no spare register is needed; i.e., if X can be directly copied to or from a register of CLASS in MODE without requiring a scratch register. Do not define this macro if it would always return `NO_REGS'.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -