📄 arm.c
字号:
/* Output routines for GCC for ARM/RISCiX. Copyright (C) 1991 Free Software Foundation, Inc. Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) and Martin Simmons (@harleqn.co.uk).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 "assert.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 "flags.h"/* The maximum number of insns skipped which will be conditionalised if possible. */#define MAX_INSNS_SKIPPED 5/* Some function declarations. */extern FILE *asm_out_file;extern char *output_multi_immediate ();extern char *arm_output_asm_insn ();extern void arm_increase_location ();/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we must report the mode of the memory reference from PRINT_OPERAND to PRINT_OPERAND_ADDRESS. */int output_memory_reference_mode;/* Nonzero if the prologue must setup `fp'. */int current_function_anonymous_args;/* Location counter of .text segment. */int arm_text_location = 0;/* A hash table is used to store text segment labels and their associated offset from the start of the text segment. */struct label_offset{ char *name; int offset; struct label_offset *cdr;};#define LABEL_HASH_SIZE 257static struct label_offset *offset_table[LABEL_HASH_SIZE];/* For an explanation of these variables, see final_prescan_insn below. */int arm_ccfsm_state;int arm_current_cc;rtx arm_target_insn;int arm_target_label;char *arm_condition_codes[];/* Return the number of mov instructions needed to get the constant VALUE into a register. */intarm_const_nmoves (value) register int value;{ register int i; if (value == 0) return (1); for (i = 0; value; i++, value &= ~0xff) while ((value & 3) == 0) value = (value >> 2) | ((value & 3) << 30); return (i);} /* arm_const_nmoves *//* Return TRUE if int I is a valid immediate ARM constant. */intconst_ok_for_arm (i) int i;{ unsigned int mask = ~0xFF; do { if ((i & mask) == 0) return(TRUE); mask = (mask << 2) | (mask >> (32 - 2)); } while (mask != ~0xFF); return (FALSE);} /* const_ok_for_arm *//* Return TRUE if rtx X is a valid immediate FPU constant. */intconst_double_rtx_ok_for_fpu (x) rtx x;{ double d; union real_extract u; u.i[0] = CONST_DOUBLE_LOW(x); u.i[1] = CONST_DOUBLE_HIGH(x); d = u.d; return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0 || d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);} /* const_double_rtx_ok_for_fpu *//* Predicates for `match_operand' and `match_operator'. *//* Return TRUE for valid operands for the rhs of an ARM instruction. */intarm_rhs_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));} /* arm_rhs_operand *//* Return TRUE for valid operands for the rhs of an FPU instruction. */intfpu_rhs_operand (op, mode) rtx op; enum machine_mode mode;{ if (register_operand (op, mode)) return(TRUE); else if (GET_CODE (op) == CONST_DOUBLE) return (const_double_rtx_ok_for_fpu (op)); else return (FALSE);} /* fpu_rhs_operand *//* Return nonzero if OP is a constant power of two. */intpower_of_two_operand (op, mode) rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT) { int value = INTVAL(op); return (value != 0 && (value & (value-1)) == 0); } return (FALSE);} /* power_of_two_operand *//* Return TRUE for a valid operand of a DImode operation. Either: REG, CONST_DOUBLE or MEM(offsettable). Note that this disallows MEM(REG+REG). */intdi_operand (op, mode) rtx op; enum machine_mode mode;{ if (register_operand (op, mode)) return (TRUE); switch (GET_CODE (op)) { case CONST_DOUBLE: case CONST_INT: return (TRUE); case MEM: return (memory_address_p (DImode, XEXP (op, 0)) && offsettable_address_p (FALSE, DImode, XEXP (op, 0))); default: return (FALSE); }} /* di_operand *//* Return TRUE for valid index operands. */intindex_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand(op, mode) || (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));} /* index_operand *//* Return TRUE for arithmetic operators which can be combined with a multiply (shift). */intshiftable_operator (x, mode) rtx x; enum machine_mode mode;{ if (GET_MODE (x) != mode) return FALSE; else { enum rtx_code code = GET_CODE (x); return (code == PLUS || code == MINUS || code == IOR || code == XOR || code == AND); }} /* shiftable_operator *//* Return TRUE for shift operators. */intshift_operator (x, mode) rtx x; enum machine_mode mode;{ if (GET_MODE (x) != mode) return FALSE; else { enum rtx_code code = GET_CODE (x); return (code == ASHIFT || code == LSHIFT || code == ASHIFTRT || code == LSHIFTRT); }} /* shift_operator *//* Routines to output assembly language. *//* Output the operands of a LDM/STM instruction to STREAM. MASK is the ARM register set mask of which only bits 0-15 are important. INSTR is the possibly suffixed base register. HAT unequals zero if a hat must follow the register list. */voidprint_multi_reg (stream, instr, mask, hat) FILE *stream; char *instr; int mask, hat;{ int i; int not_first = FALSE; fprintf (stream, "\t%s, {", instr); for (i = 0; i < 16; i++) if (mask & (1 << i)) { if (not_first) fprintf (stream, ", "); fprintf (stream, "%s", reg_names[i]); not_first = TRUE; } fprintf (stream, "}%s\n", hat ? "^" : "");} /* print_multi_reg *//* Output a 'call' insn. */char *output_call (operands) rtx operands[];{ operands[0] = XEXP (operands[0], 0); /* Handle calls to lr using ip (which may be clobbered in subr anyway). */ if (REGNO (operands[0]) == 14) { operands[0] = gen_rtx (REG, SImode, 12); arm_output_asm_insn ("mov\t%0, lr", operands); } arm_output_asm_insn ("mov\tlr, pc", operands); arm_output_asm_insn ("mov\tpc, %0", operands); return ("");} /* output_call *//* Output a move from arm registers to an fpu registers. OPERANDS[0] is an fpu register. OPERANDS[1] is the first registers of an arm register pair. */char *output_mov_double_fpu_from_arm (operands) rtx operands[];{ int arm_reg0 = REGNO (operands[1]); rtx ops[2]; if (arm_reg0 == 12) abort(); ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops); arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands); return ("");} /* output_mov_double_fpu_from_arm *//* Output a move from an fpu register to arm registers. OPERANDS[0] is the first registers of an arm register pair. OPERANDS[1] is an fpu register. */char *output_mov_double_arm_from_fpu (operands) rtx operands[];{ int arm_reg0 = REGNO (operands[0]); rtx ops[2]; if (arm_reg0 == 12) abort(); ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands); arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops); return("");} /* output_mov_double_arm_from_fpu *//* Output a move between double words. It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM or MEM<-REG and all MEMs must be offsettable addresses. */char *output_move_double (operands) rtx operands[];{ enum rtx_code code0 = GET_CODE (operands[0]); enum rtx_code code1 = GET_CODE (operands[1]); rtx otherops[2]; if (code0 == REG) { int reg0 = REGNO (operands[0]); otherops[0] = gen_rtx (REG, SImode, 1 + reg0); if (code1 == REG) { int reg1 = REGNO (operands[1]); if (reg1 == 12) abort(); otherops[1] = gen_rtx (REG, SImode, 1 + reg1); /* Ensure the second source is not overwritten */ if (reg0 == 1 + reg1) { arm_output_asm_insn("mov\t%0, %1", otherops); arm_output_asm_insn("mov\t%0, %1", operands); } else { arm_output_asm_insn("mov\t%0, %1", operands); arm_output_asm_insn("mov\t%0, %1", otherops); } } else if (code1 == CONST_DOUBLE) { otherops[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[1])); operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[1])); arm_output_asm_insn ("mov\t%0, %1", operands); arm_output_asm_insn ("mov\t%0, %1", otherops); } else if (code1 == CONST_INT) { otherops[1] = const0_rtx; arm_output_asm_insn ("mov\t%0, %1", operands); arm_output_asm_insn ("mov\t%0, %1", otherops); } else if (code1 == MEM) { if (GET_CODE (XEXP (operands[1], 0)) == REG) { /* Handle the simple case where address is [r, #0] more efficient. */ operands[1] = XEXP (operands[1], 0); arm_output_asm_insn ("ldmia\t%1, %M0", operands); } else { otherops[1] = adj_offsettable_operand (operands[1], 4); /* Take care of overlapping base/data reg. */ if (reg_mentioned_p (operands[0], operands[1])) { arm_output_asm_insn ("ldr\t%0, %1", otherops); arm_output_asm_insn ("ldr\t%0, %1", operands); } else { arm_output_asm_insn ("ldr\t%0, %1", operands); arm_output_asm_insn ("ldr\t%0, %1", otherops); } } } else abort(); /* Constraints should prevent this */ } else if (code0 == MEM && code1 == REG) { if (REGNO (operands[1]) == 12) abort(); if (GET_CODE (XEXP (operands[0], 0)) == REG) { operands[0] = XEXP (operands[0], 0); arm_output_asm_insn ("stmia\t%0, %M1", operands); } else { otherops[0] = adj_offsettable_operand (operands[0], 4); otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1])); arm_output_asm_insn ("str\t%1, %0", operands); arm_output_asm_insn ("str\t%1, %0", otherops); } } else abort(); /* Constraints should prevent this */ return("");} /* output_move_double *//* Output an arbitrary MOV reg, #n. OPERANDS[0] is a register. OPERANDS[1] is a const_int. */char *output_mov_immediate (operands) rtx operands[2];{ int n = INTVAL (operands[1]); int n_ones = 0; int i; /* Try to use one MOV */ if (const_ok_for_arm (n)) return (arm_output_asm_insn ("mov\t%0, %1", operands));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -