home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 7 / FreshFishVol7.bin / bbs / gnu / gcc-2.3.3-src.lha / GNU / src / amiga / gcc-2.3.3 / config / arm.c < prev    next >
C/C++ Source or Header  |  1994-02-06  |  36KB  |  1,344 lines

  1. /* Output routines for GCC for ARM/RISCiX.
  2.    Copyright (C) 1991 Free Software Foundation, Inc.
  3.    Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
  4.              and Martin Simmons (@harleqn.co.uk).
  5.  
  6. This file is part of GNU CC.
  7.  
  8. GNU CC is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2, or (at your option)
  11. any later version.
  12.  
  13. GNU CC is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. GNU General Public License for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with GNU CC; see the file COPYING.  If not, write to
  20. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  21.  
  22. #include <stdio.h>
  23. #include "assert.h"
  24. #include "config.h"
  25. #include "rtl.h"
  26. #include "regs.h"
  27. #include "hard-reg-set.h"
  28. #include "real.h"
  29. #include "insn-config.h"
  30. #include "conditions.h"
  31. #include "insn-flags.h"
  32. #include "output.h"
  33. #include "insn-attr.h"
  34. #include "flags.h"
  35.  
  36. /* The maximum number of insns skipped which will be conditionalised if
  37.    possible.  */
  38. #define MAX_INSNS_SKIPPED  5
  39.  
  40. /* Some function declarations.  */
  41. extern FILE *asm_out_file;
  42. extern char *output_multi_immediate ();
  43. extern char *arm_output_asm_insn ();
  44. extern void arm_increase_location ();
  45.  
  46. /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
  47.    must report the mode of the memory reference from PRINT_OPERAND to
  48.    PRINT_OPERAND_ADDRESS.  */
  49. int output_memory_reference_mode;
  50.  
  51. /* Nonzero if the prologue must setup `fp'.  */
  52. int current_function_anonymous_args;
  53.  
  54. /* Location counter of .text segment.  */
  55. int arm_text_location = 0;
  56.  
  57. /* A hash table is used to store text segment labels and their associated
  58.    offset from the start of the text segment.  */
  59. struct label_offset
  60. {
  61.   char *name;
  62.   int offset;
  63.   struct label_offset *cdr;
  64. };
  65.  
  66. #define LABEL_HASH_SIZE  257
  67.  
  68. static struct label_offset *offset_table[LABEL_HASH_SIZE];
  69.  
  70. /* For an explanation of these variables, see final_prescan_insn below.  */
  71. int arm_ccfsm_state;
  72. int arm_current_cc;
  73. rtx arm_target_insn;
  74. int arm_target_label;
  75. char *arm_condition_codes[];
  76.  
  77. /* Return the number of mov instructions needed to get the constant VALUE into
  78.    a register.  */
  79.  
  80. int
  81. arm_const_nmoves (value)
  82.      register int value;
  83. {
  84.   register int i;
  85.  
  86.   if (value == 0)
  87.     return (1);
  88.   for (i = 0; value; i++, value &= ~0xff)
  89.     while ((value & 3) == 0)
  90.       value = (value >> 2) | ((value & 3) << 30);
  91.   return (i);
  92. } /* arm_const_nmoves */
  93.  
  94.  
  95. /* Return TRUE if int I is a valid immediate ARM constant.  */
  96.  
  97. int
  98. const_ok_for_arm (i)
  99.      int i;
  100. {
  101.   unsigned int mask = ~0xFF;
  102.  
  103.   do
  104.     {
  105.       if ((i & mask) == 0)
  106.     return(TRUE);
  107.       mask = (mask << 2) | (mask >> (32 - 2));
  108.     } while (mask != ~0xFF);
  109.  
  110.   return (FALSE);
  111. } /* const_ok_for_arm */
  112.  
  113. /* Return TRUE if rtx X is a valid immediate FPU constant. */
  114.  
  115. int
  116. const_double_rtx_ok_for_fpu (x)
  117.      rtx x;
  118. {
  119.   double d;
  120.   union real_extract u;
  121.   u.i[0] = CONST_DOUBLE_LOW(x);
  122.   u.i[1] = CONST_DOUBLE_HIGH(x);
  123.   d = u.d;
  124.  
  125.   return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0
  126.       || d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);
  127. } /* const_double_rtx_ok_for_fpu */
  128.  
  129. /* Predicates for `match_operand' and `match_operator'.  */
  130.  
  131. /* Return TRUE for valid operands for the rhs of an ARM instruction.  */
  132.  
  133. int
  134. arm_rhs_operand (op, mode)
  135.      rtx op;
  136.      enum machine_mode mode;
  137. {
  138.   return (register_operand (op, mode)
  139.       || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
  140. } /* arm_rhs_operand */
  141.  
  142. /* Return TRUE for valid operands for the rhs of an FPU instruction.  */
  143.  
  144. int
  145. fpu_rhs_operand (op, mode)
  146.      rtx op;
  147.      enum machine_mode mode;
  148. {
  149.   if (register_operand (op, mode))
  150.     return(TRUE);
  151.   else if (GET_CODE (op) == CONST_DOUBLE)
  152.     return (const_double_rtx_ok_for_fpu (op));
  153.   else return (FALSE);
  154. } /* fpu_rhs_operand */
  155.  
  156. /* Return nonzero if OP is a constant power of two.  */
  157.  
  158. int
  159. power_of_two_operand (op, mode)
  160.      rtx op;
  161.      enum machine_mode mode;
  162. {
  163.   if (GET_CODE (op) == CONST_INT)
  164.     {
  165.       int value = INTVAL(op);
  166.       return (value != 0  &&  (value & (value-1)) == 0);
  167.     }
  168.   return (FALSE);
  169. } /* power_of_two_operand */
  170.  
  171. /* Return TRUE for a valid operand of a DImode operation.
  172.    Either: REG, CONST_DOUBLE or MEM(offsettable).
  173.    Note that this disallows MEM(REG+REG).  */
  174.  
  175. int
  176. di_operand (op, mode)
  177.      rtx op;
  178.      enum machine_mode mode;
  179. {
  180.   if (register_operand (op, mode))
  181.     return (TRUE);
  182.  
  183.   switch (GET_CODE (op))
  184.     {
  185.     case CONST_DOUBLE:
  186.     case CONST_INT:
  187.       return (TRUE);
  188.     case MEM:
  189.       return (memory_address_p (DImode, XEXP (op, 0))
  190.           && offsettable_address_p (FALSE, DImode, XEXP (op, 0)));
  191.     default:
  192.       return (FALSE);
  193.     }
  194. } /* di_operand */
  195.  
  196. /* Return TRUE for valid index operands. */
  197.  
  198. int
  199. index_operand (op, mode)
  200.      rtx op;
  201.      enum machine_mode mode;
  202. {
  203.   return (register_operand(op, mode)
  204.       || (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));
  205. } /* index_operand */
  206.  
  207. /* Return TRUE for arithmetic operators which can be combined with a multiply
  208.    (shift).  */
  209.  
  210. int
  211. shiftable_operator (x, mode)
  212.      rtx x;
  213.      enum machine_mode mode;
  214. {
  215.   if (GET_MODE (x) != mode)
  216.     return FALSE;
  217.   else
  218.     {
  219.       enum rtx_code code = GET_CODE (x);
  220.  
  221.       return (code == PLUS || code == MINUS
  222.           || code == IOR || code == XOR || code == AND);
  223.     }
  224. } /* shiftable_operator */
  225.  
  226. /* Return TRUE for shift operators. */
  227.  
  228. int
  229. shift_operator (x, mode)
  230.      rtx x;
  231.      enum machine_mode mode;
  232. {
  233.   if (GET_MODE (x) != mode)
  234.     return FALSE;
  235.   else
  236.     {
  237.       enum rtx_code code = GET_CODE (x);
  238.  
  239.       return (code == ASHIFT || code == LSHIFT
  240.           || code == ASHIFTRT || code == LSHIFTRT);
  241.     }
  242. } /* shift_operator */
  243.  
  244. /* Routines to output assembly language.  */
  245.  
  246. /* Output the operands of a LDM/STM instruction to STREAM.
  247.    MASK is the ARM register set mask of which only bits 0-15 are important.
  248.    INSTR is the possibly suffixed base register.  HAT unequals zero if a hat
  249.    must follow the register list.  */
  250.  
  251. void
  252. print_multi_reg (stream, instr, mask, hat)
  253.      FILE *stream;
  254.      char *instr;
  255.      int mask, hat;
  256. {
  257.   int i;
  258.   int not_first = FALSE;
  259.  
  260.   fprintf (stream, "\t%s, {", instr);
  261.   for (i = 0; i < 16; i++)
  262.     if (mask & (1 << i))
  263.       {
  264.     if (not_first)
  265.       fprintf (stream, ", ");
  266.     fprintf (stream, "%s", reg_names[i]);
  267.     not_first = TRUE;
  268.       }
  269.   fprintf (stream, "}%s\n", hat ? "^" : "");
  270. } /* print_multi_reg */
  271.  
  272. /* Output a 'call' insn. */
  273.  
  274. char *
  275. output_call (operands)
  276.     rtx operands[];
  277. {
  278.   operands[0] = XEXP (operands[0], 0);
  279.  
  280.   /* Handle calls to lr using ip (which may be clobbered in subr anyway). */
  281.  
  282.   if (REGNO (operands[0]) == 14)
  283.     {
  284.       operands[0] = gen_rtx (REG, SImode, 12);
  285.       arm_output_asm_insn ("mov\t%0, lr", operands);
  286.     }
  287.   arm_output_asm_insn ("mov\tlr, pc", operands);
  288.   arm_output_asm_insn ("mov\tpc, %0", operands);
  289.   return ("");
  290. } /* output_call */
  291.  
  292. /* Output a move from arm registers to an fpu registers.
  293.    OPERANDS[0] is an fpu register.
  294.    OPERANDS[1] is the first registers of an arm register pair.  */
  295.  
  296. char *
  297. output_mov_double_fpu_from_arm (operands)
  298.      rtx operands[];
  299. {
  300.   int arm_reg0 = REGNO (operands[1]);
  301.   rtx ops[2];
  302.  
  303.   if (arm_reg0 == 12)
  304.     abort();
  305.   ops[0] = gen_rtx (REG, SImode, arm_reg0);
  306.   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
  307.   arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);
  308.   arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);
  309.   return ("");
  310. } /* output_mov_double_fpu_from_arm */
  311.  
  312. /* Output a move from an fpu register to arm registers.
  313.    OPERANDS[0] is the first registers of an arm register pair.
  314.    OPERANDS[1] is an fpu register.  */
  315.  
  316. char *
  317. output_mov_double_arm_from_fpu (operands)
  318.      rtx operands[];
  319. {
  320.   int arm_reg0 = REGNO (operands[0]);
  321.   rtx ops[2];
  322.  
  323.   if (arm_reg0 == 12)
  324.     abort();
  325.   ops[0] = gen_rtx (REG, SImode, arm_reg0);
  326.   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
  327.   arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);
  328.   arm_output_asm_insn ("ldmfd\tsp!