Annotation of GNUtools/cc/config/arm/arm.c, revision 1.1

1.1     ! root        1: /* Output routines for GCC for ARM/RISCiX.
        !             2:    Copyright (C) 1991, 1993 Free Software Foundation, Inc.
        !             3:    Contributed by Pieter `Tiggr' Schoenmakers ([email protected])
        !             4:              and Martin Simmons (@harleqn.co.uk).
        !             5:    More major hacks by Richard Earnshaw ([email protected])
        !             6: 
        !             7: This file is part of GNU CC.
        !             8: 
        !             9: GNU CC is free software; you can redistribute it and/or modify
        !            10: it under the terms of the GNU General Public License as published by
        !            11: the Free Software Foundation; either version 2, or (at your option)
        !            12: any later version.
        !            13: 
        !            14: GNU CC is distributed in the hope that it will be useful,
        !            15: but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            16: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            17: GNU General Public License for more details.
        !            18: 
        !            19: You should have received a copy of the GNU General Public License
        !            20: along with GNU CC; see the file COPYING.  If not, write to
        !            21: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
        !            22:     
        !            23: #include <stdio.h>
        !            24: #include "assert.h"
        !            25: #include "config.h"
        !            26: #include "rtl.h"
        !            27: #include "regs.h"
        !            28: #include "hard-reg-set.h"
        !            29: #include "real.h"
        !            30: #include "insn-config.h"
        !            31: #include "conditions.h"
        !            32: #include "insn-flags.h"
        !            33: #include "output.h"
        !            34: #include "insn-attr.h"
        !            35: #include "flags.h"
        !            36: #include "reload.h"
        !            37: 
        !            38: /* The maximum number of insns skipped which will be conditionalised if
        !            39:    possible.  */
        !            40: #define MAX_INSNS_SKIPPED  5
        !            41: 
        !            42: /* Some function declarations.  */
        !            43: extern FILE *asm_out_file;
        !            44: extern char *output_multi_immediate ();
        !            45: extern char *arm_output_asm_insn ();
        !            46: extern void arm_increase_location ();
        !            47: 
        !            48: /*  Define the information needed to generate branch insns.  This is
        !            49:    stored from the compare operation. */
        !            50: 
        !            51: rtx arm_compare_op0, arm_compare_op1;
        !            52: int arm_compare_fp;
        !            53: 
        !            54: /* What type of cpu are we compiling for? */
        !            55: 
        !            56: enum processor_type arm_cpu;
        !            57: 
        !            58: /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
        !            59:    must report the mode of the memory reference from PRINT_OPERAND to
        !            60:    PRINT_OPERAND_ADDRESS.  */
        !            61: int output_memory_reference_mode;
        !            62: 
        !            63: /* Nonzero if the prologue must setup `fp'.  */
        !            64: int current_function_anonymous_args;
        !            65: 
        !            66: /* Location counter of .text segment.  */
        !            67: int arm_text_location = 0;
        !            68: 
        !            69: /* Set to one if we think that lr is only saved because of subroutine calls,
        !            70:    but all of these can be `put after' return insns */
        !            71: int lr_save_eliminated;
        !            72: 
        !            73: /* A hash table is used to store text segment labels and their associated
        !            74:    offset from the start of the text segment.  */
        !            75: struct label_offset
        !            76: {
        !            77:   char *name;
        !            78:   int offset;
        !            79:   struct label_offset *cdr;
        !            80: };
        !            81: 
        !            82: #define LABEL_HASH_SIZE  257
        !            83: 
        !            84: static struct label_offset *offset_table[LABEL_HASH_SIZE];
        !            85: 
        !            86: /* Set to 1 when a return insn is output, this means that the epilogue
        !            87:    is not needed. */
        !            88: 
        !            89: static int return_used_this_function;
        !            90: 
        !            91: /* For an explanation of these variables, see final_prescan_insn below.  */
        !            92: int arm_ccfsm_state;
        !            93: int arm_current_cc;
        !            94: rtx arm_target_insn;
        !            95: int arm_target_label;
        !            96: 
        !            97: /* Return 1 if it is possible to return using a single instruction */
        !            98: 
        !            99: int
        !           100: use_return_insn ()
        !           101: {
        !           102:   int regno;
        !           103: 
        !           104:   if (!reload_completed ||current_function_pretend_args_size
        !           105:       || current_function_anonymous_args
        !           106:       || (get_frame_size () && !(TARGET_APCS || frame_pointer_needed)))
        !           107:     return 0;
        !           108: 
        !           109:   /* Can't be done if any of the FPU regs are pushed, since this also
        !           110:      requires an insn */
        !           111:   for (regno = 20; regno < 24; regno++)
        !           112:     if (regs_ever_live[regno])
        !           113:       return 0;
        !           114: 
        !           115:   return 1;
        !           116: }
        !           117: 
        !           118: /* Return the number of mov instructions needed to get the constant VALUE into
        !           119:    a register.  */
        !           120: 
        !           121: int
        !           122: arm_const_nmoves (value)
        !           123:      register int value;
        !           124: {
        !           125:   register int i;
        !           126: 
        !           127:   if (value == 0)
        !           128:     return (1);
        !           129:   for (i = 0; value; i++, value &= ~0xff)
        !           130:     while ((value & 3) == 0)
        !           131:       value = (value >> 2) | ((value & 3) << 30);
        !           132:   return (i);
        !           133: } /* arm_const_nmoves */
        !           134: 
        !           135: 
        !           136: /* Return TRUE if int I is a valid immediate ARM constant.  */
        !           137: 
        !           138: int
        !           139: const_ok_for_arm (i)
        !           140:      HOST_WIDE_INT i;
        !           141: {
        !           142:   unsigned HOST_WIDE_INT mask = ~0xFF;
        !           143: 
        !           144:   do
        !           145:     {
        !           146:       if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0)
        !           147:         return(TRUE);
        !           148:       mask =
        !           149:          (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff)
        !           150:                         >> (32 - 2)) | ~((unsigned HOST_WIDE_INT) 0xffffffff);
        !           151:     } while (mask != ~0xFF);
        !           152: 
        !           153:   return (FALSE);
        !           154: } /* const_ok_for_arm */
        !           155: 
        !           156: /* This code has been fixed for cross compilation. */
        !           157: 
        !           158: static int fpa_consts_inited = 0;
        !           159: 
        !           160: char *strings_fpa[8] = {
        !           161:   "0.0",
        !           162:   "1.0",
        !           163:   "2.0",
        !           164:   "3.0",
        !           165:   "4.0",
        !           166:   "5.0",
        !           167:   "0.5",
        !           168:   "10.0"
        !           169:   };
        !           170: 
        !           171: static REAL_VALUE_TYPE values_fpa[8];
        !           172: 
        !           173: static void
        !           174: init_fpa_table ()
        !           175: {
        !           176:   int i;
        !           177:   REAL_VALUE_TYPE r;
        !           178: 
        !           179:   for (i = 0; i < 8; i++)
        !           180:     {
        !           181:       r = REAL_VALUE_ATOF (strings_fpa[i], DFmode);
        !           182:       values_fpa[i] = r;
        !           183:     }
        !           184:   fpa_consts_inited = 1;
        !           185: }
        !           186: 
        !           187: /* Return TRUE if rtx X is a valid immediate FPU constant. */
        !           188: 
        !           189: int
        !           190: const_double_rtx_ok_for_fpu (x)
        !           191:      rtx x;
        !           192: {
        !           193:   REAL_VALUE_TYPE r;
        !           194:   int i;
        !           195:   
        !           196:   if (!fpa_consts_inited)
        !           197:     init_fpa_table ();
        !           198:   
        !           199:   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
        !           200:   if (REAL_VALUE_MINUS_ZERO (r))
        !           201:     return 0;
        !           202:   for (i = 0; i < 8; i++)
        !           203:     if (REAL_VALUES_EQUAL (r, values_fpa[i]))
        !           204:       return 1;
        !           205:   return 0;
        !           206: } /* const_double_rtx_ok_for_fpu */
        !           207: 
        !           208: /* Return TRUE if rtx X is a valid immediate FPU constant. */
        !           209: 
        !           210: int
        !           211: neg_const_double_rtx_ok_for_fpu (x)
        !           212:      rtx x;
        !           213: {
        !           214:   REAL_VALUE_TYPE r;
        !           215:   int i;
        !           216:   
        !           217:   if (!fpa_consts_inited)
        !           218:     init_fpa_table ();
        !           219:   
        !           220:   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
        !           221:   r = REAL_VALUE_NEGATE (r);
        !           222:   if (REAL_VALUE_MINUS_ZERO (r))
        !           223:     return 0;
        !           224:   for (i = 0; i < 8; i++)
        !           225:     if (REAL_VALUES_EQUAL (r, values_fpa[i]))
        !           226:       return 1;
        !           227:   return 0;
        !           228: } /* neg_const_double_rtx_ok_for_fpu */
        !           229: 
        !           230: /* Predicates for `match_operand' and `match_operator'.  */
        !           231: 
        !           232: /* s_register_operand is the same as register_operand, but it doesn't accept
        !           233:    (SUBREG (MEM)...). */
        !           234: 
        !           235: int
        !           236: s_register_operand (op, mode)
        !           237:      register rtx op;
        !           238:      enum machine_mode mode;
        !           239: {
        !           240:   if (GET_MODE (op) != mode && mode != VOIDmode)
        !           241:     return 0;
        !           242: 
        !           243:   if (GET_CODE (op) == SUBREG)
        !           244:     {
        !           245:       op = SUBREG_REG (op);
        !           246:     }
        !           247: 
        !           248:   /* We don't consider registers whose class is NO_REGS
        !           249:      to be a register operand.  */
        !           250:   return (GET_CODE (op) == REG
        !           251:          && (REGNO (op) >= FIRST_PSEUDO_REGISTER
        !           252:              || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
        !           253: }
        !           254: 
        !           255: /* Return 1 if OP is an item in memory, given that we are in reload.  */
        !           256: 
        !           257: int
        !           258: reload_memory_operand (op, mode)
        !           259:      rtx op;
        !           260:      enum machine_mode mode;
        !           261: {
        !           262:   int regno = true_regnum (op);
        !           263: 
        !           264:   return (! CONSTANT_P (op)
        !           265:          && (regno == -1
        !           266:              || (GET_CODE (op) == REG
        !           267:                  && REGNO (op) >= FIRST_PSEUDO_REGISTER)));
        !           268: }
        !           269: 
        !           270: /* Return TRUE for valid operands for the rhs of an ARM instruction.  */
        !           271: 
        !           272: int
        !           273: arm_rhs_operand (op, mode)
        !           274:      rtx op;
        !           275:      enum machine_mode mode;
        !           276: {
        !           277:   return (s_register_operand (op, mode)
        !           278:          || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
        !           279: } /* arm_rhs_operand */
        !           280: 
        !           281: /* Return TRUE for valid operands for the rhs of an ARM instruction, or a load.
        !           282:  */
        !           283: 
        !           284: int
        !           285: arm_rhsm_operand (op, mode)
        !           286:      rtx op;
        !           287:      enum machine_mode mode;
        !           288: {
        !           289:   return (s_register_operand (op, mode)
        !           290:          || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))
        !           291:          || memory_operand (op, mode));
        !           292: } /* arm_rhs_operand */
        !           293: 
        !           294: /* Return TRUE for valid operands for the rhs of an ARM instruction, or if a
        !           295:    constant that is valid when negated.  */
        !           296: 
        !           297: int
        !           298: arm_add_operand (op, mode)
        !           299:      rtx op;
        !           300:      enum machine_mode mode;
        !           301: {
        !           302:   return (s_register_operand (op, mode)
        !           303:          || (GET_CODE (op) == CONST_INT
        !           304:              && (const_ok_for_arm (INTVAL (op))
        !           305:                  || const_ok_for_arm (-INTVAL (op)))));
        !           306: } /* arm_rhs_operand */
        !           307: 
        !           308: int
        !           309: arm_not_operand (op, mode)
        !           310:      rtx op;
        !           311:      enum machine_mode mode;
        !           312: {
        !           313:   return (s_register_operand (op, mode)
        !           314:          || (GET_CODE (op) == CONST_INT
        !           315:              && (const_ok_for_arm (INTVAL (op))
        !           316:                  || const_ok_for_arm (~INTVAL (op)))));
        !           317: } /* arm_rhs_operand */
        !           318: 
        !           319: /* Return TRUE for valid operands for the rhs of an FPU instruction.  */
        !           320: 
        !           321: int
        !           322: fpu_rhs_operand (op, mode)
        !           323:      rtx op;
        !           324:      enum machine_mode mode;
        !           325: {
        !           326:   if (s_register_operand (op, mode))
        !           327:     return(TRUE);
        !           328:   else if (GET_CODE (op) == CONST_DOUBLE)
        !           329:     return (const_double_rtx_ok_for_fpu (op));
        !           330:   else return (FALSE);
        !           331: } /* fpu_rhs_operand */
        !           332: 
        !           333: int
        !           334: fpu_add_operand (op, mode)
        !           335:      rtx op;
        !           336:      enum machine_mode mode;
        !           337: {
        !           338:   if (s_register_operand (op, mode))
        !           339:     return(TRUE);
        !           340:   else if (GET_CODE (op) == CONST_DOUBLE)
        !           341:     return const_double_rtx_ok_for_fpu (op) 
        !           342:        || neg_const_double_rtx_ok_for_fpu (op);
        !           343:   return (FALSE);
        !           344: }
        !           345: 
        !           346: /* Return nonzero if OP is a constant power of two.  */
        !           347: 
        !           348: int
        !           349: power_of_two_operand (op, mode)
        !           350:      rtx op;
        !           351:      enum machine_mode mode;
        !           352: {
        !           353:   if (GET_CODE (op) == CONST_INT)
        !           354:     {
        !           355:       int value = INTVAL(op);
        !           356:       return (value != 0  &&  (value & (value-1)) == 0);
        !           357:     }
        !           358:   return (FALSE);
        !           359: } /* power_of_two_operand */
        !           360: 
        !           361: /* Return TRUE for a valid operand of a DImode operation.
        !           362:    Either: REG, CONST_DOUBLE or MEM(DImode_address).
        !           363:    Note that this disallows MEM(REG+REG), but allows
        !           364:    MEM(PRE/POST_INC/DEC(REG)).  */
        !           365: 
        !           366: int
        !           367: di_operand (op, mode)
        !           368:      rtx op;
        !           369:      enum machine_mode mode;
        !           370: {
        !           371:   if (s_register_operand (op, mode))
        !           372:     return (TRUE);
        !           373: 
        !           374:   switch (GET_CODE (op))
        !           375:     {
        !           376:     case CONST_DOUBLE:
        !           377:     case CONST_INT:
        !           378:       return (TRUE);
        !           379:     case MEM:
        !           380:       return (memory_address_p (DImode, XEXP (op, 0)));
        !           381:     default:
        !           382:       return (FALSE);
        !           383:     }
        !           384: } /* di_operand */
        !           385: 
        !           386: /* Return TRUE for valid index operands. */
        !           387: 
        !           388: int
        !           389: index_operand (op, mode)
        !           390:      rtx op;
        !           391:      enum machine_mode mode;
        !           392: {
        !           393:   return (s_register_operand(op, mode)
        !           394:          || (immediate_operand (op, mode)
        !           395:              && INTVAL (op) < 4096 && INTVAL (op) > -4096));
        !           396: } /* index_operand */
        !           397: 
        !           398: /* Return TRUE for valid shifts by a constant. This also accepts any
        !           399:    power of two on the (somewhat overly relaxed) assumption that the
        !           400:    shift operator in this case was a mult. */
        !           401: 
        !           402: int
        !           403: const_shift_operand (op, mode)
        !           404:      rtx op;
        !           405:      enum machine_mode mode;
        !           406: {
        !           407:   return (power_of_two_operand (op, mode)
        !           408:          || (immediate_operand (op, mode)
        !           409:              && (INTVAL (op) < 32 && INTVAL (op) > 0)));
        !           410: } /* const_shift_operand */
        !           411: 
        !           412: /* Return TRUE for arithmetic operators which can be combined with a multiply
        !           413:    (shift).  */
        !           414: 
        !           415: int
        !           416: shiftable_operator (x, mode)
        !           417:      rtx x;
        !           418:      enum machine_mode mode;
        !           419: {
        !           420:   if (GET_MODE (x) != mode)
        !           421:     return FALSE;
        !           422:   else
        !           423:     {
        !           424:       enum rtx_code code = GET_CODE (x);
        !           425: 
        !           426:       return (code == PLUS || code == MINUS
        !           427:              || code == IOR || code == XOR || code == AND);
        !           428:     }
        !           429: } /* shiftable_operator */
        !           430: 
        !           431: /* Return TRUE for shift operators. */
        !           432: 
        !           433: int
        !           434: shift_operator (x, mode)
        !           435:      rtx x;
        !           436:      enum machine_mode mode;
        !           437: {
        !           438:   if (GET_MODE (x) != mode)
        !           439:     return FALSE;
        !           440:   else
        !           441:     {
        !           442:       enum rtx_code code = GET_CODE (x);
        !           443: 
        !           444:       if (code == MULT)
        !           445:        return power_of_two_operand (XEXP (x, 1));
        !           446:       return (code == ASHIFT || code == LSHIFT
        !           447:              || code == ASHIFTRT || code == LSHIFTRT);
        !           448:     }
        !           449: } /* shift_operator */
        !           450: 
        !           451: int equality_operator (x, mode)
        !           452: rtx x;
        !           453: enum machine_mode mode;
        !           454: {
        !           455:   return (GET_CODE (x) == EQ || GET_CODE (x) == NE);
        !           456: }
        !           457: 
        !           458: /* Return TRUE for SMIN SMAX UMIN UMAX operators. */
        !           459: 
        !           460: int
        !           461: minmax_operator (x, mode)
        !           462:      rtx x;
        !           463:      enum machine_mode mode;
        !           464: {
        !           465:   enum rtx_code code = GET_CODE (x);
        !           466: 
        !           467:   if (GET_MODE (x) != mode)
        !           468:     return FALSE;
        !           469:   return code == SMIN || code == SMAX || code == UMIN || code == UMAX;
        !           470: } /* minmax_operator */
        !           471: 
        !           472: /* return TRUE if x is EQ or NE */
        !           473: 
        !           474: /* Return TRUE if this is the condition code register, if we aren't given
        !           475:    a mode, accept any class CCmode register */
        !           476: 
        !           477: int
        !           478: cc_register (x, mode)
        !           479: rtx x;
        !           480: enum machine_mode mode;
        !           481: {
        !           482:   if (mode == VOIDmode)
        !           483:     {
        !           484:       mode = GET_MODE (x);
        !           485:       if (GET_MODE_CLASS (mode) != MODE_CC)
        !           486:        return FALSE;
        !           487:     }
        !           488:   if (mode == GET_MODE (x) && GET_CODE (x) == REG && REGNO (x) == 24)
        !           489:     return TRUE;
        !           490:   return FALSE;
        !           491: }
        !           492:        
        !           493: enum rtx_code
        !           494: minmax_code (x)
        !           495: rtx x;
        !           496: {
        !           497:   enum rtx_code code = GET_CODE (x);
        !           498: 
        !           499:   if (code == SMAX)
        !           500:     return GE;
        !           501:   if (code == SMIN)
        !           502:     return LE;
        !           503:   if (code == UMIN)
        !           504:     return LEU;
        !           505:   if (code == UMAX)
        !           506:     return GEU;
        !           507:   abort ();
        !           508: }
        !           509: 
        !           510: /* Return 1 if memory locations are adjacent */
        !           511: 
        !           512: adjacent_mem_locations (a, b)
        !           513:      rtx a, b;
        !           514: {
        !           515:   int val0 = 0, val1 = 0;
        !           516:   int reg0, reg1;
        !           517:   
        !           518:   if ((GET_CODE (XEXP (a, 0)) == REG
        !           519:        || (GET_CODE (XEXP (a, 0)) == PLUS
        !           520:           && GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT))
        !           521:       && (GET_CODE (XEXP (b, 0)) == REG
        !           522:          || (GET_CODE (XEXP (b, 0)) == PLUS
        !           523:              && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
        !           524:     {
        !           525:       if (GET_CODE (XEXP (a, 0)) == PLUS)
        !           526:         {
        !           527:          reg0 = REGNO (XEXP (XEXP (a, 0), 0));
        !           528:          val0 = INTVAL (XEXP (XEXP (a, 0), 1));
        !           529:         }
        !           530:       else
        !           531:        reg0 = REGNO (XEXP (a, 0));
        !           532:       if (GET_CODE (XEXP (b, 0)) == PLUS)
        !           533:         {
        !           534:          reg1 = REGNO (XEXP (XEXP (b, 0), 0));
        !           535:          val1 = INTVAL (XEXP (XEXP (b, 0), 1));
        !           536:         }
        !           537:       else
        !           538:        reg1 = REGNO (XEXP (b, 0));
        !           539:       return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4);
        !           540:     }
        !           541:   return 0;
        !           542: }
        !           543: 
        !           544: /* Return 1 if OP is a load multiple operation.  It is known to be
        !           545:    parallel and the first section will be tested. */
        !           546: 
        !           547: load_multiple_operation (op, mode)
        !           548:      rtx op;
        !           549:      enum machine_mode mode;
        !           550: {
        !           551:   int count = XVECLEN (op, 0);
        !           552:   int dest_regno;
        !           553:   rtx src_addr;
        !           554:   int i = 1, base = 0;
        !           555:   rtx elt;
        !           556: 
        !           557:   if (count <= 1
        !           558:       || GET_CODE (XVECEXP (op, 0, 0)) != SET)
        !           559:     return 0;
        !           560: 
        !           561:   /* Check to see if this might be a write-back */
        !           562:   if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
        !           563:     {
        !           564:       i++;
        !           565:       base = 1;
        !           566: 
        !           567:       /* Now check it more carefully */
        !           568:       if (GET_CODE (SET_DEST (elt)) != REG
        !           569:           || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
        !           570:           || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
        !           571:           || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
        !           572:           || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 2) * 4
        !           573:           || GET_CODE (XVECEXP (op, 0, count - 1)) != CLOBBER
        !           574:           || GET_CODE (XEXP (XVECEXP (op, 0, count - 1), 0)) != REG
        !           575:           || REGNO (XEXP (XVECEXP (op, 0, count - 1), 0))
        !           576:               != REGNO (SET_DEST (elt)))
        !           577:         return 0;
        !           578:       count--;
        !           579:     }
        !           580: 
        !           581:   /* Perform a quick check so we don't blow up below.  */
        !           582:   if (count <= i
        !           583:       || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
        !           584:       || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG
        !           585:       || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM)
        !           586:     return 0;
        !           587: 
        !           588:   dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
        !           589:   src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
        !           590: 
        !           591:   for (; i < count; i++)
        !           592:     {
        !           593:       rtx elt = XVECEXP (op, 0, i);
        !           594: 
        !           595:       if (GET_CODE (elt) != SET
        !           596:           || GET_CODE (SET_DEST (elt)) != REG
        !           597:           || GET_MODE (SET_DEST (elt)) != SImode
        !           598:           || REGNO (SET_DEST (elt)) != dest_regno + i - base
        !           599:           || GET_CODE (SET_SRC (elt)) != MEM
        !           600:           || GET_MODE (SET_SRC (elt)) != SImode
        !           601:           || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
        !           602:           || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
        !           603:           || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
        !           604:           || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4)
        !           605:         return 0;
        !           606:     }
        !           607: 
        !           608:   return 1;
        !           609: }
        !           610: 
        !           611: /* Return 1 if OP is a store multiple operation.  It is known to be
        !           612:    parallel and the first section will be tested. */
        !           613: 
        !           614: store_multiple_operation (op, mode)
        !           615:      rtx op;
        !           616:      enum machine_mode mode;
        !           617: {
        !           618:   int count = XVECLEN (op, 0);
        !           619:   int src_regno;
        !           620:   rtx dest_addr;
        !           621:   int i = 1, base = 0;
        !           622:   rtx elt;
        !           623: 
        !           624:   if (count <= 1
        !           625:       || GET_CODE (XVECEXP (op, 0, 0)) != SET)
        !           626:     return 0;
        !           627: 
        !           628:   /* Check to see if this might be a write-back */
        !           629:   if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
        !           630:     {
        !           631:       i++;
        !           632:       base = 1;
        !           633: 
        !           634:       /* Now check it more carefully */
        !           635:       if (GET_CODE (SET_DEST (elt)) != REG
        !           636:           || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
        !           637:           || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
        !           638:           || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
        !           639:           || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 2) * 4
        !           640:           || GET_CODE (XVECEXP (op, 0, count - 1)) != CLOBBER
        !           641:           || GET_CODE (XEXP (XVECEXP (op, 0, count - 1), 0)) != REG
        !           642:           || REGNO (XEXP (XVECEXP (op, 0, count - 1), 0))
        !           643:               != REGNO (SET_DEST (elt)))
        !           644:         return 0;
        !           645:       count--;
        !           646:     }
        !           647: 
        !           648:   /* Perform a quick check so we don't blow up below.  */
        !           649:   if (count <= i
        !           650:       || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
        !           651:       || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM
        !           652:       || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG)
        !           653:     return 0;
        !           654: 
        !           655:   src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
        !           656:   dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
        !           657: 
        !           658:   for (; i < count; i++)
        !           659:     {
        !           660:       elt = XVECEXP (op, 0, i);
        !           661: 
        !           662:       if (GET_CODE (elt) != SET
        !           663:           || GET_CODE (SET_SRC (elt)) != REG
        !           664:           || GET_MODE (SET_SRC (elt)) != SImode
        !           665:           || REGNO (SET_SRC (elt)) != src_regno + i - base
        !           666:           || GET_CODE (SET_DEST (elt)) != MEM
        !           667:           || GET_MODE (SET_DEST (elt)) != SImode
        !           668:           || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
        !           669:           || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
        !           670:           || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
        !           671:           || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4)
        !           672:         return 0;
        !           673:     }
        !           674: 
        !           675:   return 1;
        !           676: }
        !           677: 
        !           678: /* Routines for use in generating RTL */
        !           679: 
        !           680: rtx arm_gen_load_multiple (base_regno, count, from, up, write_back)
        !           681:      int base_regno;
        !           682:      int count;
        !           683:      rtx from;
        !           684:      int up;
        !           685:      int write_back;
        !           686: {
        !           687:   int i = 0, j;
        !           688:   rtx result;
        !           689:   int sign = up ? 1 : -1;
        !           690: 
        !           691:   result = gen_rtx (PARALLEL, VOIDmode,
        !           692:                     rtvec_alloc (count + (write_back ? 2 : 0)));
        !           693:   if (write_back)
        !           694:   {
        !           695:       XVECEXP (result, 0, 0)
        !           696:           = gen_rtx (SET, GET_MODE (from), from,
        !           697:                      plus_constant (from, count * 4 * sign));
        !           698:       i = 1;
        !           699:       count++;
        !           700:   }
        !           701:   for (j = 0; i < count; i++, j++)
        !           702:   {
        !           703:       XVECEXP (result, 0, i)
        !           704:           = gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, base_regno + j),
        !           705:                      gen_rtx (MEM, SImode,
        !           706:                               plus_constant (from, j * 4 * sign)));
        !           707:   }
        !           708:   if (write_back)
        !           709:     XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, from);
        !           710: 
        !           711:   return result;
        !           712: }
        !           713: 
        !           714: rtx arm_gen_store_multiple (base_regno, count, to, up, write_back)
        !           715:      int base_regno;
        !           716:      int count;
        !           717:      rtx to;
        !           718:      int up;
        !           719:      int write_back;
        !           720: {
        !           721:   int i = 0, j;
        !           722:   rtx result;
        !           723:   int sign = up ? 1 : -1;
        !           724: 
        !           725:   result = gen_rtx (PARALLEL, VOIDmode,
        !           726:                     rtvec_alloc (count + (write_back ? 2 : 0)));
        !           727:   if (write_back)
        !           728:   {
        !           729:       XVECEXP (result, 0, 0)
        !           730:           = gen_rtx (SET, GET_MODE (to), to,
        !           731:                      plus_constant (to, count * 4 * sign));
        !           732:       i = 1;
        !           733:       count++;
        !           734:   }
        !           735:   for (j = 0; i < count; i++, j++)
        !           736:   {
        !           737:       XVECEXP (result, 0, i)
        !           738:           = gen_rtx (SET, VOIDmode,
        !           739:                      gen_rtx (MEM, SImode, plus_constant (to, j * 4 * sign)),
        !           740:                      gen_rtx (REG, SImode, base_regno + j));
        !           741:   }
        !           742:   if (write_back)
        !           743:     XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, to);
        !           744: 
        !           745:   return result;
        !           746: }
        !           747: 
        !           748: /* X and Y are two things to compare using CODE.  Emit the compare insn and
        !           749:    return the rtx for register 0 in the proper mode.  FP means this is a
        !           750:    floating point compare: I don't think that it is needed on the arm.  */
        !           751: 
        !           752: rtx
        !           753: gen_compare_reg (code, x, y, fp)
        !           754:      enum rtx_code code;
        !           755:      rtx x, y;
        !           756: {
        !           757:   enum machine_mode mode = SELECT_CC_MODE (code, x, y);
        !           758:   rtx cc_reg = gen_rtx (REG, mode, 24);
        !           759: 
        !           760:   emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
        !           761:                       gen_rtx (COMPARE, mode, x, y)));
        !           762: 
        !           763:   return cc_reg;
        !           764: }
        !           765: 
        !           766: arm_reload_out_hi (operands)
        !           767: rtx operands[];
        !           768: {
        !           769:   rtx base = find_replacement (&XEXP (operands[0], 0));
        !           770: 
        !           771:   emit_insn (gen_rtx (SET, VOIDmode,
        !           772:                       gen_rtx (MEM, QImode, base),
        !           773:                       gen_rtx (SUBREG, QImode, operands[1], 0)));
        !           774:   emit_insn (gen_rtx (SET, VOIDmode, operands[2],
        !           775:                       gen_rtx (LSHIFTRT, SImode, 
        !           776:                                gen_rtx (SUBREG, SImode, operands[1], 0),
        !           777:                                GEN_INT (8))));
        !           778:   emit_insn (gen_rtx (SET, VOIDmode,
        !           779:                       gen_rtx (MEM, QImode,
        !           780:                                plus_constant (base, 1)),
        !           781:                       gen_rtx (SUBREG, QImode, operands[2], 0)));
        !           782: }
        !           783: 
        !           784: /* Check to see if a branch is forwards or backwards.  Return TRUE if it
        !           785:    is backwards.  */
        !           786: 
        !           787: int
        !           788: arm_backwards_branch (from, to)
        !           789: int from, to;
        !           790: {
        !           791:   return (insn_addresses[to] <= insn_addresses[from]);
        !           792: }
        !           793: 
        !           794: /* Check to see if a branch is within the distance that can be done using
        !           795:    an arithmetic expression. */
        !           796: int
        !           797: short_branch (from, to)
        !           798: int from, to;
        !           799: {
        !           800:   int delta = insn_addresses[from] + 2 - insn_addresses[to];
        !           801: 
        !           802:   return abs (delta) < 245;    /* A small margin for safety */
        !           803: }
        !           804: 
        !           805: /* Check to see that the insn isn't the target of the conditionalizing
        !           806:    code */
        !           807: int
        !           808: arm_insn_not_targeted (insn)
        !           809: rtx insn;
        !           810: {
        !           811:   return insn != arm_target_insn;
        !           812: }
        !           813: 
        !           814: 
        !           815: /* Routines to output assembly language.  */
        !           816: 
        !           817: /* fp_immediate_constant 
        !           818:    if the rtx is the correct value then return the string of the number.
        !           819:    In this way we can ensure that valid double constants are generated even
        !           820:    when cross compiling. */
        !           821: char *
        !           822: fp_immediate_constant (x)
        !           823: rtx (x);
        !           824: {
        !           825:   REAL_VALUE_TYPE r;
        !           826:   int i;
        !           827:   
        !           828:   if (!fpa_consts_inited)
        !           829:     init_fpa_table ();
        !           830:   
        !           831:   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
        !           832:   for (i = 0; i < 8; i++)
        !           833:     if (REAL_VALUES_EQUAL (r, values_fpa[i]))
        !           834:       return strings_fpa[i];
        !           835:   abort ();
        !           836: }
        !           837: 
        !           838: 
        !           839: /* Output the operands of a LDM/STM instruction to STREAM.
        !           840:    MASK is the ARM register set mask of which only bits 0-15 are important.
        !           841:    INSTR is the possibly suffixed base register.  HAT unequals zero if a hat
        !           842:    must follow the register list.  */
        !           843: 
        !           844: void
        !           845: print_multi_reg (stream, instr, mask, hat)
        !           846:      FILE *stream;
        !           847:      char *instr;
        !           848:      int mask, hat;
        !           849: {
        !           850:   int i;
        !           851:   int not_first = FALSE;
        !           852: 
        !           853:   fprintf (stream, "\t%s, {", instr);
        !           854:   for (i = 0; i < 16; i++)
        !           855:     if (mask & (1 << i))
        !           856:       {
        !           857:        if (not_first)
        !           858:          fprintf (stream, ", ");
        !           859:        fprintf (stream, "%s", reg_names[i]);
        !           860:        not_first = TRUE;
        !           861:       }
        !           862:   fprintf (stream, "}%s\n", hat ? "^" : "");
        !           863: } /* print_multi_reg */
        !           864: 
        !           865: /* Output a 'call' insn. */
        !           866: 
        !           867: char *
        !           868: output_call (operands)
        !           869:        rtx operands[];
        !           870: {
        !           871:   /* Handle calls to lr using ip (which may be clobbered in subr anyway). */
        !           872: 
        !           873:   if (REGNO (operands[0]) == 14)
        !           874:     {
        !           875:       operands[0] = gen_rtx (REG, SImode, 12);
        !           876:       arm_output_asm_insn ("mov\t%0, lr", operands);
        !           877:     }
        !           878:   arm_output_asm_insn ("mov\tlr, pc", operands);
        !           879:   arm_output_asm_insn ("mov\tpc, %0", operands);
        !           880:   return ("");
        !           881: } /* output_call */
        !           882: 
        !           883: static int
        !           884: eliminate_lr2ip (x)
        !           885: rtx *x;
        !           886: {
        !           887:   int something_changed = 0;
        !           888:   rtx x0 = *x;
        !           889:   int code = GET_CODE (x0);
        !           890:   register int i, j;
        !           891:   register char *fmt;
        !           892:   
        !           893:   switch (code)
        !           894:     {
        !           895:     case REG:
        !           896:       if (REGNO (x0) == 14)
        !           897:         {
        !           898:          *x = gen_rtx (REG, SImode, 12);
        !           899:          return 1;
        !           900:         }
        !           901:       return 0;
        !           902:     default:
        !           903:       /* Scan through the sub-elements and change any references there */
        !           904:       fmt = GET_RTX_FORMAT (code);
        !           905:       for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
        !           906:        if (fmt[i] == 'e')
        !           907:          something_changed |= eliminate_lr2ip (&XEXP (x0, i));
        !           908:        else if (fmt[i] == 'E')
        !           909:          for (j = 0; j < XVECLEN (x0, i); j++)
        !           910:            something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j));
        !           911:       return something_changed;
        !           912:     }
        !           913: }
        !           914:   
        !           915: /* Output a 'call' insn that is a reference in memory. */
        !           916: 
        !           917: char *
        !           918: output_call_mem (operands)
        !           919:        rtx operands[];
        !           920: {
        !           921:   operands[0] = copy_rtx (operands[0]); /* Be ultra careful */
        !           922:   /* Handle calls using lr by using ip (which may be clobbered in subr anyway).
        !           923:    */
        !           924:   if (eliminate_lr2ip (&operands[0]))
        !           925:     arm_output_asm_insn ("mov\tip, lr", operands);
        !           926:   arm_output_asm_insn ("mov\tlr, pc", operands);
        !           927:   arm_output_asm_insn ("ldr\tpc, %0", operands);
        !           928:   return ("");
        !           929: } /* output_call */
        !           930: 
        !           931: 
        !           932: /* Output a move from arm registers to an fpu registers.
        !           933:    OPERANDS[0] is an fpu register.
        !           934:    OPERANDS[1] is the first registers of an arm register pair.  */
        !           935: 
        !           936: char *
        !           937: output_mov_long_double_fpu_from_arm (operands)
        !           938:      rtx operands[];
        !           939: {
        !           940:   int arm_reg0 = REGNO (operands[1]);
        !           941:   rtx ops[3];
        !           942: 
        !           943:   if (arm_reg0 == 12)
        !           944:     abort();
        !           945:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
        !           946:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
        !           947:   ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0);
        !           948:   
        !           949:   arm_output_asm_insn ("stmfd\tsp!, {%0, %1, %2}", ops);
        !           950:   arm_output_asm_insn ("ldfe\t%0, [sp], #12", operands);
        !           951:   return ("");
        !           952: } /* output_mov_long_double_fpu_from_arm */
        !           953: 
        !           954: /* Output a move from an fpu register to arm registers.
        !           955:    OPERANDS[0] is the first registers of an arm register pair.
        !           956:    OPERANDS[1] is an fpu register.  */
        !           957: 
        !           958: char *
        !           959: output_mov_long_double_arm_from_fpu (operands)
        !           960:      rtx operands[];
        !           961: {
        !           962:   int arm_reg0 = REGNO (operands[0]);
        !           963:   rtx ops[3];
        !           964: 
        !           965:   if (arm_reg0 == 12)
        !           966:     abort();
        !           967:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
        !           968:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
        !           969:   ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0);
        !           970: 
        !           971:   arm_output_asm_insn ("stfe\t%1, [sp, #-12]!", operands);
        !           972:   arm_output_asm_insn ("ldmfd\tsp!, {%0, %1, %2}", ops);
        !           973:   return("");
        !           974: } /* output_mov_long_double_arm_from_fpu */
        !           975: 
        !           976: /* Output a move from arm registers to arm registers of a long double
        !           977:    OPERANDS[0] is the destination.
        !           978:    OPERANDS[1] is the source.  */
        !           979: char *
        !           980: output_mov_long_double_arm_from_arm (operands)
        !           981: rtx operands[];
        !           982: {
        !           983:   /* We have to be careful here because the two might overlap */
        !           984:   int dest_start = REGNO (operands[0]);
        !           985:   int src_start = REGNO (operands[1]);
        !           986:   rtx ops[2];
        !           987:   int i;
        !           988: 
        !           989:   if (dest_start < src_start)
        !           990:     {
        !           991:       for (i = 0; i < 3; i++)
        !           992:        {
        !           993:          ops[0] = gen_rtx (REG, SImode, dest_start + i);
        !           994:          ops[1] = gen_rtx (REG, SImode, src_start + i);
        !           995:          arm_output_asm_insn ("mov\t%0, %1", ops);
        !           996:        }
        !           997:     }
        !           998:   else
        !           999:     {
        !          1000:       for (i = 2; i >= 0; i--)
        !          1001:        {
        !          1002:          ops[0] = gen_rtx (REG, SImode, dest_start + i);
        !          1003:          ops[1] = gen_rtx (REG, SImode, src_start + i);
        !          1004:          arm_output_asm_insn ("mov\t%0, %1", ops);
        !          1005:        }
        !          1006:     }
        !          1007:   return "";
        !          1008: }
        !          1009: 
        !          1010: 
        !          1011: /* Output a move from arm registers to an fpu registers.
        !          1012:    OPERANDS[0] is an fpu register.
        !          1013:    OPERANDS[1] is the first registers of an arm register pair.  */
        !          1014: 
        !          1015: char *
        !          1016: output_mov_double_fpu_from_arm (operands)
        !          1017:      rtx operands[];
        !          1018: {
        !          1019:   int arm_reg0 = REGNO (operands[1]);
        !          1020:   rtx ops[2];
        !          1021: 
        !          1022:   if (arm_reg0 == 12)
        !          1023:     abort();
        !          1024:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
        !          1025:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
        !          1026:   arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);
        !          1027:   arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);
        !          1028:   return ("");
        !          1029: } /* output_mov_double_fpu_from_arm */
        !          1030: 
        !          1031: /* Output a move from an fpu register to arm registers.
        !          1032:    OPERANDS[0] is the first registers of an arm register pair.
        !          1033:    OPERANDS[1] is an fpu register.  */
        !          1034: 
        !          1035: char *
        !          1036: output_mov_double_arm_from_fpu (operands)
        !          1037:      rtx operands[];
        !          1038: {
        !          1039:   int arm_reg0 = REGNO (operands[0]);
        !          1040:   rtx ops[2];
        !          1041: 
        !          1042:   if (arm_reg0 == 12)
        !          1043:     abort();
        !          1044:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
        !          1045:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
        !          1046:   arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);
        !          1047:   arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops);
        !          1048:   return("");
        !          1049: } /* output_mov_double_arm_from_fpu */
        !          1050: 
        !          1051: /* Output a move between double words.
        !          1052:    It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM
        !          1053:    or MEM<-REG and all MEMs must be offsettable addresses.  */
        !          1054: 
        !          1055: char *
        !          1056: output_move_double (operands)
        !          1057:      rtx operands[];
        !          1058: {
        !          1059:   enum rtx_code code0 = GET_CODE (operands[0]);
        !          1060:   enum rtx_code code1 = GET_CODE (operands[1]);
        !          1061:   rtx otherops[2];
        !          1062: 
        !          1063:   if (code0 == REG)
        !          1064:     {
        !          1065:       int reg0 = REGNO (operands[0]);
        !          1066: 
        !          1067:       otherops[0] = gen_rtx (REG, SImode, 1 + reg0);
        !          1068:       if (code1 == REG)
        !          1069:        {
        !          1070:          int reg1 = REGNO (operands[1]);
        !          1071:          if (reg1 == 12)
        !          1072:            abort();
        !          1073:          otherops[1] = gen_rtx (REG, SImode, 1 + reg1);
        !          1074: 
        !          1075:          /* Ensure the second source is not overwritten */
        !          1076:          if (reg0 == 1 + reg1)
        !          1077:            {
        !          1078:              arm_output_asm_insn("mov\t%0, %1", otherops);
        !          1079:              arm_output_asm_insn("mov\t%0, %1", operands);
        !          1080:            }
        !          1081:          else
        !          1082:            {
        !          1083:              arm_output_asm_insn("mov\t%0, %1", operands);
        !          1084:              arm_output_asm_insn("mov\t%0, %1", otherops);
        !          1085:            }
        !          1086:        }
        !          1087:       else if (code1 == CONST_DOUBLE)
        !          1088:        {
        !          1089:          otherops[1] = gen_rtx (CONST_INT, VOIDmode,
        !          1090:                                 CONST_DOUBLE_HIGH (operands[1]));
        !          1091:          operands[1] = gen_rtx (CONST_INT, VOIDmode,
        !          1092:                                 CONST_DOUBLE_LOW (operands[1]));
        !          1093:          output_mov_immediate (operands, FALSE, "");
        !          1094:          output_mov_immediate (otherops, FALSE, "");
        !          1095:        }
        !          1096:       else if (code1 == CONST_INT)
        !          1097:        {
        !          1098:          otherops[1] = const0_rtx;
        !          1099:          /* sign extend the intval into the high-order word */
        !          1100:          /* Note: output_mov_immediate may clobber operands[1], so we
        !          1101:             put this out first */
        !          1102:          if (INTVAL (operands[1]) < 0)
        !          1103:            arm_output_asm_insn ("mvn\t%0, %1", otherops);
        !          1104:          else
        !          1105:            arm_output_asm_insn ("mov\t%0, %1", otherops);
        !          1106:          output_mov_immediate (operands, FALSE, "");
        !          1107:        }
        !          1108:       else if (code1 == MEM)
        !          1109:        {
        !          1110:          switch (GET_CODE (XEXP (operands[1], 0)))
        !          1111:            {
        !          1112:            case REG:
        !          1113:              /* Handle the simple case where address is [r, #0] more
        !          1114:                 efficient.  */
        !          1115:              operands[1] = XEXP (operands[1], 0);
        !          1116:              arm_output_asm_insn ("ldmia\t%1, %M0", operands);
        !          1117:              break;
        !          1118:            case PRE_INC:
        !          1119:              operands[1] = XEXP (XEXP (operands[1], 0), 0);
        !          1120:              arm_output_asm_insn ("add\t%1, %1, #8", operands);
        !          1121:              arm_output_asm_insn ("ldmia\t%1, %M0", operands);
        !          1122:              break;
        !          1123:            case PRE_DEC:
        !          1124:              operands[1] = XEXP (XEXP (operands[1], 0), 0);
        !          1125:              arm_output_asm_insn ("sub\t%1, %1, #8", operands);
        !          1126:              arm_output_asm_insn ("ldmia\t%1, %M0", operands);
        !          1127:              break;
        !          1128:            case POST_INC:
        !          1129:              operands[1] = XEXP (XEXP (operands[1], 0), 0);
        !          1130:              arm_output_asm_insn ("ldmia\t%1!, %M0", operands);
        !          1131:              break;
        !          1132:            case POST_DEC:
        !          1133:              operands[1] = XEXP (XEXP (operands[1], 0), 0);
        !          1134:              arm_output_asm_insn ("ldmia\t%1, %M0", operands);
        !          1135:              arm_output_asm_insn ("sub\t%1, %1, #8", operands);
        !          1136:              break;
        !          1137:            default:
        !          1138:              otherops[1] = adj_offsettable_operand (operands[1], 4);
        !          1139:              /* Take care of overlapping base/data reg.  */
        !          1140:              if (reg_mentioned_p (operands[0], operands[1]))
        !          1141:                {
        !          1142:                  arm_output_asm_insn ("ldr\t%0, %1", otherops);
        !          1143:                  arm_output_asm_insn ("ldr\t%0, %1", operands);
        !          1144:                }
        !          1145:              else
        !          1146:                {
        !          1147:                  arm_output_asm_insn ("ldr\t%0, %1", operands);
        !          1148:                  arm_output_asm_insn ("ldr\t%0, %1", otherops);
        !          1149:                }
        !          1150:            }
        !          1151:        }
        !          1152:       else abort();  /* Constraints should prevent this */
        !          1153:     }
        !          1154:   else if (code0 == MEM && code1 == REG)
        !          1155:     {
        !          1156:       if (REGNO (operands[1]) == 12)
        !          1157:        abort();
        !          1158:       switch (GET_CODE (XEXP (operands[0], 0)))
        !          1159:         {
        !          1160:        case REG:
        !          1161:          operands[0] = XEXP (operands[0], 0);
        !          1162:          arm_output_asm_insn ("stmia\t%0, %M1", operands);
        !          1163:          break;
        !          1164:         case PRE_INC:
        !          1165:          operands[0] = XEXP (XEXP (operands[0], 0), 0);
        !          1166:          arm_output_asm_insn ("add\t%0, %0, #8", operands);
        !          1167:          arm_output_asm_insn ("stmia\t%0, %M1", operands);
        !          1168:          break;
        !          1169:         case PRE_DEC:
        !          1170:          operands[0] = XEXP (XEXP (operands[0], 0), 0);
        !          1171:          arm_output_asm_insn ("sub\t%0, %0, #8", operands);
        !          1172:          arm_output_asm_insn ("stmia\t%0, %M1", operands);
        !          1173:          break;
        !          1174:         case POST_INC:
        !          1175:          operands[0] = XEXP (XEXP (operands[0], 0), 0);
        !          1176:          arm_output_asm_insn ("stmia\t%0!, %M1", operands);
        !          1177:          break;
        !          1178:         case POST_DEC:
        !          1179:          operands[0] = XEXP (XEXP (operands[0], 0), 0);
        !          1180:          arm_output_asm_insn ("stmia\t%0, %M1", operands);
        !          1181:          arm_output_asm_insn ("sub\t%0, %0, #8", operands);
        !          1182:          break;
        !          1183:         default:
        !          1184:          otherops[0] = adj_offsettable_operand (operands[0], 4);
        !          1185:          otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
        !          1186:          arm_output_asm_insn ("str\t%1, %0", operands);
        !          1187:          arm_output_asm_insn ("str\t%1, %0", otherops);
        !          1188:        }
        !          1189:     }
        !          1190:   else abort();  /* Constraints should prevent this */
        !          1191: 
        !          1192:   return("");
        !          1193: } /* output_move_double */
        !          1194: 
        !          1195: 
        !          1196: /* Output an arbitrary MOV reg, #n.
        !          1197:    OPERANDS[0] is a register.  OPERANDS[1] is a const_int.  */
        !          1198: 
        !          1199: char *
        !          1200: output_mov_immediate (operands)
        !          1201:      rtx operands[2];
        !          1202: {
        !          1203:   int n = INTVAL (operands[1]);
        !          1204:   int n_ones = 0;
        !          1205:   int i;
        !          1206: 
        !          1207:   /* Try to use one MOV */
        !          1208: 
        !          1209:   if (const_ok_for_arm (n))
        !          1210:     return (arm_output_asm_insn ("mov\t%0, %1", operands));
        !          1211: 
        !          1212:   /* Try to use one MVN */
        !          1213: 
        !          1214:   if (const_ok_for_arm(~n))
        !          1215:     {
        !          1216:       operands[1] = gen_rtx (CONST_INT, VOIDmode, ~n);
        !          1217:       return (arm_output_asm_insn ("mvn\t%0, %1", operands));
        !          1218:     }
        !          1219: 
        !          1220:   /* If all else fails, make it out of ORRs or BICs as appropriate. */
        !          1221: 
        !          1222:   for (i=0; i < 32; i++)
        !          1223:     if (n & 1 << i)
        !          1224:       n_ones++;
        !          1225: 
        !          1226:   if (n_ones > 16)  /* Shorter to use MVN with BIC in this case. */
        !          1227:     output_multi_immediate(operands, "mvn\t%0, %1", "bic\t%0, %0, %1", 1, ~n);
        !          1228:   else
        !          1229:     output_multi_immediate(operands, "mov\t%0, %1", "orr\t%0, %0, %1", 1, n);
        !          1230:   return("");
        !          1231: } /* output_mov_immediate */
        !          1232: 
        !          1233: 
        !          1234: /* Output an ADD r, s, #n where n may be too big for one instruction.  If
        !          1235:    adding zero to one register, output nothing.  */
        !          1236: 
        !          1237: char *
        !          1238: output_add_immediate (operands)
        !          1239:      rtx operands[3];
        !          1240: {
        !          1241:   int n = INTVAL (operands[2]);
        !          1242: 
        !          1243:   if (n != 0 || REGNO (operands[0]) != REGNO (operands[1]))
        !          1244:     {
        !          1245:       if (n < 0)
        !          1246:        output_multi_immediate (operands,
        !          1247:                                "sub\t%0, %1, %2", "sub\t%0, %0, %2", 2, -n);
        !          1248:       else
        !          1249:        output_multi_immediate (operands,
        !          1250:                                "add\t%0, %1, %2", "add\t%0, %0, %2", 2, n);
        !          1251:     }
        !          1252:   return("");
        !          1253: } /* output_add_immediate */
        !          1254: 
        !          1255: 
        !          1256: /* Output a multiple immediate operation.
        !          1257:    OPERANDS is the vector of operands referred to in the output patterns.
        !          1258:    INSTR1 is the output pattern to use for the first constant.
        !          1259:    INSTR2 is the output pattern to use for subsequent constants.
        !          1260:    IMMED_OP is the index of the constant slot in OPERANDS.
        !          1261:    N is the constant value.  */
        !          1262: 
        !          1263: char *
        !          1264: output_multi_immediate (operands, instr1, instr2, immed_op, n)
        !          1265:      rtx operands[];
        !          1266:      char *instr1, *instr2;
        !          1267:      int immed_op, n;
        !          1268: {
        !          1269:   if (n == 0)
        !          1270:     {
        !          1271:       operands[immed_op] = const0_rtx;
        !          1272:       arm_output_asm_insn (instr1, operands); /* Quick and easy output */
        !          1273:     }
        !          1274:   else
        !          1275:     {
        !          1276:       int i;
        !          1277:       char *instr = instr1;
        !          1278: 
        !          1279:       /* Note that n is never zero here (which would give no output) */
        !          1280: 
        !          1281:       for (i = 0; i < 32; i += 2)
        !          1282:        {
        !          1283:          if (n & (3 << i))
        !          1284:            {
        !          1285:              operands[immed_op] = gen_rtx (CONST_INT, VOIDmode,
        !          1286:                                            n & (255 << i));
        !          1287:              arm_output_asm_insn (instr, operands);
        !          1288:              instr = instr2;
        !          1289:              i += 6;
        !          1290:            }
        !          1291:        }
        !          1292:     }
        !          1293:   return ("");
        !          1294: } /* output_multi_immediate */
        !          1295: 
        !          1296: 
        !          1297: /* Return the appropriate ARM instruction for the operation code.
        !          1298:    The returned result should not be overwritten.  OP is the rtx of the
        !          1299:    operation.  SHIFT_FIRST_ARG is TRUE if the first argument of the operator
        !          1300:    was shifted.  */
        !          1301: 
        !          1302: char *
        !          1303: arithmetic_instr (op, shift_first_arg)
        !          1304:      rtx op;
        !          1305: {
        !          1306:   switch (GET_CODE(op))
        !          1307:     {
        !          1308:     case PLUS:
        !          1309:       return ("add");
        !          1310:     case MINUS:
        !          1311:       if (shift_first_arg)
        !          1312:        return ("rsb");
        !          1313:       else
        !          1314:        return ("sub");
        !          1315:     case IOR:
        !          1316:       return ("orr");
        !          1317:     case XOR:
        !          1318:       return ("eor");
        !          1319:     case AND:
        !          1320:       return ("and");
        !          1321:     default:
        !          1322:       abort();
        !          1323:     }
        !          1324:   return ("");                 /* stupid cc */
        !          1325: } /* arithmetic_instr */
        !          1326: 
        !          1327: 
        !          1328: /* Ensure valid constant shifts and return the appropriate shift mnemonic
        !          1329:    for the operation code.  The returned result should not be overwritten.
        !          1330:    OP is the rtx code of the shift.
        !          1331:    SHIFT_PTR points to the shift size operand.  */
        !          1332: 
        !          1333: char *
        !          1334: shift_instr (op, shift_ptr)
        !          1335:      enum rtx_code op;
        !          1336:      rtx *shift_ptr;
        !          1337: {
        !          1338:   int min_shift = 0;
        !          1339:   int max_shift = 31;
        !          1340:   char *mnem;
        !          1341: 
        !          1342:   switch (op)
        !          1343:     {
        !          1344:     case ASHIFT:
        !          1345:       mnem = "asl";
        !          1346:       break;
        !          1347:     case LSHIFT:
        !          1348:       mnem = "lsl";
        !          1349:       break;
        !          1350:     case ASHIFTRT:
        !          1351:       mnem = "asr";
        !          1352:       max_shift = 32;
        !          1353:       break;
        !          1354:     case LSHIFTRT:
        !          1355:       mnem = "lsr";
        !          1356:       max_shift = 32;
        !          1357:       break;
        !          1358:     case MULT:
        !          1359:       *shift_ptr = gen_rtx (CONST_INT, VOIDmode,
        !          1360:                            int_log2 (INTVAL (*shift_ptr)));
        !          1361:       return ("asl");
        !          1362:     default:
        !          1363:       abort();
        !          1364:     }
        !          1365: 
        !          1366:   if (GET_CODE (*shift_ptr) == CONST_INT)
        !          1367:     {
        !          1368:       int shift = INTVAL (*shift_ptr);
        !          1369: 
        !          1370:       if (shift < min_shift)
        !          1371:        *shift_ptr = gen_rtx (CONST_INT, VOIDmode, 0);
        !          1372:       else if (shift > max_shift)
        !          1373:        *shift_ptr = gen_rtx (CONST_INT, VOIDmode, max_shift);
        !          1374:     }
        !          1375:   return (mnem);
        !          1376: } /* shift_instr */
        !          1377: 
        !          1378: 
        !          1379: /* Obtain the shift from the POWER of two. */
        !          1380: 
        !          1381: int
        !          1382: int_log2 (power)
        !          1383:      unsigned int power;
        !          1384: {
        !          1385:   int shift = 0;
        !          1386: 
        !          1387:   while (((1 << shift) & power) == 0)
        !          1388:     {
        !          1389:       if (shift > 31)
        !          1390:        abort();
        !          1391:       shift++;
        !          1392:     }
        !          1393:   return (shift);
        !          1394: } /* int_log2 */
        !          1395: 
        !          1396: 
        !          1397: /* Output an arithmetic instruction which may set the condition code.
        !          1398:    OPERANDS[0] is the destination register.
        !          1399:    OPERANDS[1] is the arithmetic operator expression.
        !          1400:    OPERANDS[2] is the left hand argument.
        !          1401:    OPERANDS[3] is the right hand argument.
        !          1402:    CONST_FIRST_ARG is TRUE if the first argument of the operator was constant.
        !          1403:    SET_COND is TRUE when the condition code should be set.  */
        !          1404: 
        !          1405: char *
        !          1406: output_arithmetic (operands, const_first_arg, set_cond)
        !          1407:      rtx operands[4];
        !          1408:      int const_first_arg;
        !          1409:      int set_cond;
        !          1410: {
        !          1411:   char mnemonic[80];
        !          1412:   char *instr = arithmetic_instr (operands[1], const_first_arg);
        !          1413: 
        !          1414:   sprintf (mnemonic, "%s%s\t%%0, %%2, %%3", instr, set_cond ? "s" : "");
        !          1415:   return (arm_output_asm_insn (mnemonic, operands));
        !          1416: } /* output_arithmetic */
        !          1417: 
        !          1418: 
        !          1419: /* Output an arithmetic instruction with a shift.
        !          1420:    OPERANDS[0] is the destination register.
        !          1421:    OPERANDS[1] is the arithmetic operator expression.
        !          1422:    OPERANDS[2] is the unshifted register.
        !          1423:    OPERANDS[3] is the shift operator expression.
        !          1424:    OPERANDS[4] is the shifted register.
        !          1425:    OPERANDS[5] is the shift constant or register.
        !          1426:    SHIFT_FIRST_ARG is TRUE if the first argument of the operator was shifted.
        !          1427:    SET_COND is TRUE when the condition code should be set.  */
        !          1428: 
        !          1429: char *
        !          1430: output_arithmetic_with_shift (operands, shift_first_arg, set_cond)
        !          1431:      rtx operands[6];
        !          1432:      int shift_first_arg;
        !          1433:      int set_cond;
        !          1434: {
        !          1435:   char mnemonic[80];
        !          1436:   char *instr = arithmetic_instr (operands[1], shift_first_arg);
        !          1437:   char *condbit = set_cond ? "s" : "";
        !          1438:   char *shift = shift_instr (GET_CODE (operands[3]), &operands[5]);
        !          1439: 
        !          1440:   sprintf (mnemonic, "%s%s\t%%0, %%2, %%4, %s %%5", instr, condbit, shift);
        !          1441:   return (arm_output_asm_insn (mnemonic, operands));
        !          1442: } /* output_arithmetic_with_shift */
        !          1443: 
        !          1444: 
        !          1445: /* Output an arithmetic instruction with a power of two multiplication.
        !          1446:    OPERANDS[0] is the destination register.
        !          1447:    OPERANDS[1] is the arithmetic operator expression.
        !          1448:    OPERANDS[2] is the unmultiplied register.
        !          1449:    OPERANDS[3] is the multiplied register.
        !          1450:    OPERANDS[4] is the constant multiple (power of two).
        !          1451:    SHIFT_FIRST_ARG is TRUE if the first arg of the operator was multiplied.  */
        !          1452: 
        !          1453: char *
        !          1454: output_arithmetic_with_immediate_multiply (operands, shift_first_arg)
        !          1455:      rtx operands[5];
        !          1456:      int shift_first_arg;
        !          1457: {
        !          1458:   char mnemonic[80];
        !          1459:   char *instr = arithmetic_instr (operands[1], shift_first_arg);
        !          1460:   int shift = int_log2 (INTVAL (operands[4]));
        !          1461: 
        !          1462:   sprintf (mnemonic, "%s\t%%0, %%2, %%3, asl#%d", instr, shift);
        !          1463:   return (arm_output_asm_insn (mnemonic, operands));
        !          1464: } /* output_arithmetic_with_immediate_multiply */
        !          1465: 
        !          1466: 
        !          1467: /* Output a move with a shift.
        !          1468:    OP is the shift rtx code.
        !          1469:    OPERANDS[0] = destination register.
        !          1470:    OPERANDS[1] = source register.
        !          1471:    OPERANDS[2] = shift constant or register.  */
        !          1472: 
        !          1473: char *
        !          1474: output_shifted_move (op, operands)
        !          1475:      enum rtx_code op;
        !          1476:      rtx operands[2];
        !          1477: {
        !          1478:   char mnemonic[80];
        !          1479: 
        !          1480:   if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0)
        !          1481:     sprintf (mnemonic, "mov\t%%0, %%1");
        !          1482:   else
        !          1483:     sprintf (mnemonic, "mov\t%%0, %%1, %s %%2",
        !          1484:             shift_instr (op, &operands[2]));
        !          1485:   return (arm_output_asm_insn (mnemonic, operands));
        !          1486: } /* output_shifted_move */
        !          1487: 
        !          1488: char *
        !          1489: output_shift_compare (operands, neg)
        !          1490: rtx *operands;
        !          1491: int neg;
        !          1492: {
        !          1493:   char buf[80];
        !          1494: 
        !          1495:   if (neg)
        !          1496:     sprintf (buf, "cmn\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]),
        !          1497:                                                        &operands[4]));
        !          1498:   else
        !          1499:     sprintf (buf, "cmp\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]),
        !          1500:                                                        &operands[4]));
        !          1501:   return arm_output_asm_insn (buf, operands);
        !          1502: } /* output_shift_compare */
        !          1503: 
        !          1504: /* Output a .ascii pseudo-op, keeping track of lengths.  This is because
        !          1505:    /bin/as is horribly restrictive.  */
        !          1506: 
        !          1507: void
        !          1508: output_ascii_pseudo_op (stream, p, len)
        !          1509:      FILE *stream;
        !          1510:      unsigned char *p;
        !          1511:      int len;
        !          1512: {
        !          1513:   int i;
        !          1514:   int len_so_far = 1000;
        !          1515:   int chars_so_far = 0;
        !          1516: 
        !          1517:   for (i = 0; i < len; i++)
        !          1518:     {
        !          1519:       register int c = p[i];
        !          1520: 
        !          1521:       if (len_so_far > 50)
        !          1522:        {
        !          1523:          if (chars_so_far)
        !          1524:            fputs ("\"\n", stream);
        !          1525:          fputs ("\t.ascii\t\"", stream);
        !          1526:          len_so_far = 0;
        !          1527:          arm_increase_location (chars_so_far);
        !          1528:          chars_so_far = 0;
        !          1529:        }
        !          1530: 
        !          1531:       if (c == '\"' || c == '\\')
        !          1532:        {
        !          1533:          putc('\\', stream);
        !          1534:          len_so_far++;
        !          1535:        }
        !          1536:       if (c >= ' ' && c < 0177)
        !          1537:        {
        !          1538:          putc (c, stream);
        !          1539:          len_so_far++;
        !          1540:        }
        !          1541:       else
        !          1542:        {
        !          1543:          fprintf (stream, "\\%03o", c);
        !          1544:          len_so_far +=4;
        !          1545:        }
        !          1546:       chars_so_far++;
        !          1547:     }
        !          1548:   fputs ("\"\n", stream);
        !          1549:   arm_increase_location (chars_so_far);
        !          1550: } /* output_ascii_pseudo_op */
        !          1551: 
        !          1552: 
        !          1553: /* Try to determine whether a pattern really clobbers the link register.
        !          1554:    This information is useful when peepholing, so that lr need not be pushed
        !          1555:    if we combine a call followed by a return.
        !          1556:    NOTE: This code does not check for side-effect expressions in a SET_SRC:
        !          1557:    such a check should not be needed because these only update an existing
        !          1558:    value within a register; the register must still be set elsewhere within
        !          1559:    the function. */
        !          1560: 
        !          1561: static int
        !          1562: pattern_really_clobbers_lr (x)
        !          1563: rtx x;
        !          1564: {
        !          1565:   int i;
        !          1566:   
        !          1567:   switch (GET_CODE (x))
        !          1568:     {
        !          1569:     case SET:
        !          1570:       switch (GET_CODE (SET_DEST (x)))
        !          1571:        {
        !          1572:        case REG:
        !          1573:          return REGNO (SET_DEST (x)) == 14;
        !          1574:         case SUBREG:
        !          1575:          if (GET_CODE (XEXP (SET_DEST (x), 0)) == REG)
        !          1576:            return REGNO (XEXP (SET_DEST (x), 0)) == 14;
        !          1577:          if (GET_CODE (XEXP (SET_DEST (x), 0)) == MEM)
        !          1578:            return 0;
        !          1579:          abort ();
        !          1580:         default:
        !          1581:          return 0;
        !          1582:         }
        !          1583:     case PARALLEL:
        !          1584:       for (i = 0; i < XVECLEN (x, 0); i++)
        !          1585:        if (pattern_really_clobbers_lr (XVECEXP (x, 0, i)))
        !          1586:          return 1;
        !          1587:       return 0;
        !          1588:     case CLOBBER:
        !          1589:       switch (GET_CODE (XEXP (x, 0)))
        !          1590:         {
        !          1591:        case REG:
        !          1592:          return REGNO (XEXP (x, 0)) == 14;
        !          1593:         case SUBREG:
        !          1594:          if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG)
        !          1595:            return REGNO (XEXP (XEXP (x, 0), 0)) == 14;
        !          1596:          abort ();
        !          1597:         default:
        !          1598:          return 0;
        !          1599:         }
        !          1600:     case UNSPEC:
        !          1601:       return 1;
        !          1602:     default:
        !          1603:       return 0;
        !          1604:     }
        !          1605: }
        !          1606: 
        !          1607: static int
        !          1608: function_really_clobbers_lr (first)
        !          1609: rtx first;
        !          1610: {
        !          1611:   rtx insn, next;
        !          1612:   
        !          1613:   for (insn = first; insn; insn = next_nonnote_insn (insn))
        !          1614:     {
        !          1615:       switch (GET_CODE (insn))
        !          1616:         {
        !          1617:        case BARRIER:
        !          1618:        case NOTE:
        !          1619:        case CODE_LABEL:
        !          1620:        case JUMP_INSN:         /* Jump insns only change the PC (and conds) */
        !          1621:        case INLINE_HEADER:
        !          1622:          break;
        !          1623:         case INSN:
        !          1624:          if (pattern_really_clobbers_lr (PATTERN (insn)))
        !          1625:            return 1;
        !          1626:          break;
        !          1627:         case CALL_INSN:
        !          1628:          /* Don't yet know how to handle those calls that are not to a 
        !          1629:             SYMBOL_REF */
        !          1630:          if (GET_CODE (PATTERN (insn)) != PARALLEL)
        !          1631:            abort ();
        !          1632:          switch (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)))
        !          1633:            {
        !          1634:            case CALL:
        !          1635:              if (GET_CODE (XEXP (XEXP (XVECEXP (PATTERN (insn), 0, 0), 0), 0))
        !          1636:                  != SYMBOL_REF)
        !          1637:                return 1;
        !          1638:              break;
        !          1639:            case SET:
        !          1640:              if (GET_CODE (XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn),
        !          1641:                                                          0, 0)), 0), 0))
        !          1642:                  != SYMBOL_REF)
        !          1643:                return 1;
        !          1644:              break;
        !          1645:            default:    /* Don't recognize it, be safe */
        !          1646:              return 1;
        !          1647:            }
        !          1648:          /* A call can be made (by peepholing) not to clobber lr iff it is
        !          1649:             followed by a return.  There may, however, be a use insn iff
        !          1650:             we are returning the result of the call. 
        !          1651:             If we run off the end of the insn chain, then that means the
        !          1652:             call was at the end of the function.  Unfortunately we don't
        !          1653:             have a return insn for the peephole to recognize, so we
        !          1654:             must reject this.  (Can this be fixed by adding our own insn?) */
        !          1655:          if ((next = next_nonnote_insn (insn)) == NULL)
        !          1656:            return 1;
        !          1657:          if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == USE
        !          1658:              && (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
        !          1659:              && (REGNO (SET_DEST (XVECEXP (PATTERN (insn), 0, 0)))
        !          1660:                  == REGNO (XEXP (PATTERN (next), 0))))
        !          1661:            if ((next = next_nonnote_insn (next)) == NULL)
        !          1662:              return 1;
        !          1663:          if (GET_CODE (next) == JUMP_INSN
        !          1664:              && GET_CODE (PATTERN (next)) == RETURN)
        !          1665:            break;
        !          1666:          return 1;
        !          1667:         default:
        !          1668:          abort ();
        !          1669:         }
        !          1670:     }
        !          1671:   /* We have reached the end of the chain so lr was _not_ clobbered */
        !          1672:   return 0;
        !          1673: }
        !          1674: 
        !          1675: char *
        !          1676: output_return_instruction (operand, really_return)
        !          1677: rtx operand;
        !          1678: int really_return;
        !          1679: {
        !          1680:   char instr[100];
        !          1681:   int reg, live_regs = 0;
        !          1682: 
        !          1683:   if (current_function_calls_alloca && !really_return)
        !          1684:     abort();
        !          1685:     
        !          1686:   for (reg = 4; reg < 10; reg++)
        !          1687:     if (regs_ever_live[reg])
        !          1688:       live_regs++;
        !          1689: 
        !          1690:   if (live_regs || (regs_ever_live[14] && !lr_save_eliminated))
        !          1691:     live_regs++;
        !          1692: 
        !          1693:   if (frame_pointer_needed)
        !          1694:     live_regs += 4;
        !          1695: 
        !          1696:   if (live_regs)
        !          1697:     {
        !          1698:       if (lr_save_eliminated || !regs_ever_live[14])
        !          1699:         live_regs++;
        !          1700:       if (frame_pointer_needed)
        !          1701:         strcpy (instr, "ldm%d0ea\tfp, {");
        !          1702:       else
        !          1703:         strcpy (instr, "ldm%d0fd\tsp!, {");
        !          1704:       for (reg = 4; reg < 10; reg++)
        !          1705:         if (regs_ever_live[reg])
        !          1706:           {
        !          1707:             strcat (instr, reg_names[reg]);
        !          1708:            if (--live_regs)
        !          1709:               strcat (instr, ", ");
        !          1710:           }
        !          1711:       if (frame_pointer_needed)
        !          1712:         {
        !          1713:           strcat (instr, reg_names[11]);
        !          1714:           strcat (instr, ", ");
        !          1715:           strcat (instr, reg_names[13]);
        !          1716:           strcat (instr, ", ");
        !          1717:           strcat (instr, really_return ? reg_names[15] : reg_names[14]);
        !          1718:         }
        !          1719:       else
        !          1720:         strcat (instr, really_return ? reg_names[15] : reg_names[14]);
        !          1721:       strcat (instr, (TARGET_6 || !really_return) ? "}" : "}^");
        !          1722:       arm_output_asm_insn (instr, &operand);
        !          1723:     }
        !          1724:   else if (really_return)
        !          1725:     {
        !          1726:       strcpy (instr, TARGET_6 ? "mov%d0\tpc, lr" : "mov%d0s\tpc, lr");
        !          1727:       arm_output_asm_insn (instr, &operand);
        !          1728:     }
        !          1729:   return_used_this_function = 1;
        !          1730:   return "";
        !          1731: }
        !          1732: 
        !          1733: /* The amount of stack adjustment that happens here, in output_return and in
        !          1734:    output_epilogue must be exactly the same as was calculated during reload,
        !          1735:    or things will point to the wrong place.  The only time we can safely
        !          1736:    ignore this constraint is when a function has no arguments on the stack,
        !          1737:    no stack frame requirement and no live registers execpt for `lr'.  If we
        !          1738:    can guarantee that by making all function calls into tail calls and that
        !          1739:    lr is not clobbered in any other way, then there is no need to push lr
        !          1740:    onto the stack. */
        !          1741:    
        !          1742: void
        !          1743: output_prologue (f, frame_size)
        !          1744:      FILE *f;
        !          1745:      int frame_size;
        !          1746: {
        !          1747:   int reg, live_regs_mask = 0, code_size = 0;
        !          1748:   rtx operands[3];
        !          1749: 
        !          1750:   /* Nonzero if we must stuff some register arguments onto the stack as if
        !          1751:      they were passed there.  */
        !          1752:   int store_arg_regs = 0;
        !          1753: 
        !          1754:   if (arm_ccfsm_state || arm_target_insn)
        !          1755:     abort ();                                  /* Sanity check */
        !          1756:   
        !          1757:   return_used_this_function = 0;
        !          1758:   lr_save_eliminated = 0;
        !          1759:   
        !          1760:   fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n",
        !          1761:           current_function_args_size, current_function_pretend_args_size,
        !          1762:           frame_size);
        !          1763:   fprintf (f, "\t@ frame_needed = %d, current_function_anonymous_args = %d\n",
        !          1764:           frame_pointer_needed, current_function_anonymous_args);
        !          1765: 
        !          1766:   if (current_function_anonymous_args && current_function_pretend_args_size)
        !          1767:     store_arg_regs = 1;
        !          1768: 
        !          1769:   for (reg = 4; reg < 10; reg++)
        !          1770:     if (regs_ever_live[reg])
        !          1771:       live_regs_mask |= (1 << reg);
        !          1772: 
        !          1773:   if (frame_pointer_needed)
        !          1774:     {
        !          1775:       live_regs_mask |= 0xD800;
        !          1776:       fputs ("\tmov\tip, sp\n", f);
        !          1777:       code_size += 4;
        !          1778:     }
        !          1779:   else if (regs_ever_live[14])
        !          1780:     {
        !          1781:       if (! current_function_args_size
        !          1782:          && !function_really_clobbers_lr (get_insns ()))
        !          1783:        {
        !          1784:          fprintf (f,"\t@ I don't think this function clobbers lr\n");
        !          1785:          lr_save_eliminated = 1;
        !          1786:         }
        !          1787:       else
        !          1788:         live_regs_mask |= 0x4000;
        !          1789:     }
        !          1790: 
        !          1791:   /* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
        !          1792:      room.  If also STORE_ARG_REGS store the argument registers involved in
        !          1793:      the created slot (this is for stdarg and varargs).  */
        !          1794:   if (current_function_pretend_args_size)
        !          1795:     {
        !          1796:       if (store_arg_regs)
        !          1797:        {
        !          1798:          int arg_size, mask = 0;
        !          1799: 
        !          1800:          assert (current_function_pretend_args_size <= 16);
        !          1801:          for (reg = 3, arg_size = current_function_pretend_args_size;
        !          1802:               arg_size > 0; reg--, arg_size -= 4)
        !          1803:            mask |= (1 << reg);
        !          1804:          print_multi_reg (f, "stmfd\tsp!", mask, FALSE);
        !          1805:          code_size += 4;
        !          1806:        }
        !          1807:       else
        !          1808:        {
        !          1809:          operands[0] = operands[1] = stack_pointer_rtx;
        !          1810:          operands[2] = gen_rtx (CONST_INT, VOIDmode,
        !          1811:                                 -current_function_pretend_args_size);
        !          1812:          output_add_immediate (operands);
        !          1813:        }
        !          1814:     }
        !          1815: 
        !          1816:   if (live_regs_mask)
        !          1817:     {
        !          1818:       /* if a di mode load/store multiple is used, and the base register
        !          1819:         is r3, then r4 can become an ever live register without lr
        !          1820:         doing so,  in this case we need to push lr as well, or we
        !          1821:         will fail to get a proper return. */
        !          1822: 
        !          1823:       live_regs_mask |= 0x4000;
        !          1824:       lr_save_eliminated = 0;
        !          1825:       print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE);
        !          1826:       code_size += 4;
        !          1827:     }
        !          1828: 
        !          1829:   for (reg = 23; reg > 19; reg--)
        !          1830:     if (regs_ever_live[reg])
        !          1831:       {
        !          1832:        fprintf (f, "\tstfe\t%s, [sp, #-12]!\n", reg_names[reg]);
        !          1833:        code_size += 4;
        !          1834:       }
        !          1835: 
        !          1836:   if (frame_pointer_needed)
        !          1837:     {
        !          1838:       /* Make `fp' point to saved value of `pc'. */
        !          1839: 
        !          1840:       operands[0] = gen_rtx (REG, SImode, HARD_FRAME_POINTER_REGNUM);
        !          1841:       operands[1] = gen_rtx (REG, SImode, 12);
        !          1842:       operands[2] = gen_rtx (CONST_INT, VOIDmode,
        !          1843:                             - (4 + current_function_pretend_args_size));
        !          1844:       output_add_immediate (operands);
        !          1845:     }
        !          1846: 
        !          1847:   if (frame_size)
        !          1848:     {
        !          1849:       operands[0] = operands[1] = stack_pointer_rtx;
        !          1850:       operands[2] = gen_rtx (CONST_INT, VOIDmode, -frame_size);
        !          1851:       output_add_immediate (operands);
        !          1852:     }
        !          1853: 
        !          1854:   arm_increase_location (code_size);
        !          1855: } /* output_prologue */
        !          1856: 
        !          1857: 
        !          1858: void
        !          1859: output_epilogue (f, frame_size)
        !          1860:      FILE *f;
        !          1861:      int frame_size;
        !          1862: {
        !          1863:   int reg, live_regs_mask = 0, code_size = 0;
        !          1864:   /* If we need this then it will always be at lesat this much */
        !          1865:   int floats_offset = 24;
        !          1866:   rtx operands[3];
        !          1867: 
        !          1868:   if (use_return_insn() && return_used_this_function)
        !          1869:     {
        !          1870:       if (frame_size && !(frame_pointer_needed || TARGET_APCS))
        !          1871:         {
        !          1872:           abort ();
        !          1873:         }
        !          1874:       return;
        !          1875:     }
        !          1876: 
        !          1877:   for (reg = 4; reg <= 10; reg++)
        !          1878:     if (regs_ever_live[reg])
        !          1879:       {
        !          1880:         live_regs_mask |= (1 << reg);
        !          1881:        floats_offset += 4;
        !          1882:       }
        !          1883: 
        !          1884: 
        !          1885:   if (frame_pointer_needed)
        !          1886:     {
        !          1887:       for (reg = 23; reg >= 20; reg--)
        !          1888:        if (regs_ever_live[reg])
        !          1889:          {
        !          1890:            fprintf (f, "\tldfe\t%s, [fp, #-%d]\n", reg_names[reg],
        !          1891:                     floats_offset);
        !          1892:            floats_offset += 12;
        !          1893:            code_size += 4;
        !          1894:          }
        !          1895: 
        !          1896:       live_regs_mask |= 0xA800;
        !          1897:       print_multi_reg (f, "ldmea\tfp", live_regs_mask,
        !          1898:                       TARGET_6 ? FALSE : TRUE);
        !          1899:       code_size += 4;
        !          1900:     }
        !          1901:   else
        !          1902:     {
        !          1903:       /* Restore stack pointer if necessary.  */
        !          1904:       if (frame_size)
        !          1905:        {
        !          1906:          operands[0] = operands[1] = stack_pointer_rtx;
        !          1907:          operands[2] = gen_rtx (CONST_INT, VOIDmode, frame_size);
        !          1908:          output_add_immediate (operands);
        !          1909:        }
        !          1910: 
        !          1911:       for (reg = 20; reg < 24; reg++)
        !          1912:        if (regs_ever_live[reg])
        !          1913:          {
        !          1914:            fprintf (f, "\tldfe\t%s, [sp], #12\n", reg_names[reg]);
        !          1915:            code_size += 4;
        !          1916:          }
        !          1917:       if (current_function_pretend_args_size == 0 && regs_ever_live[14])
        !          1918:        {
        !          1919:          print_multi_reg (f, "ldmfd\tsp!", live_regs_mask | 0x8000,
        !          1920:                           TARGET_6 ? FALSE : TRUE);
        !          1921:          code_size += 4;
        !          1922:        }
        !          1923:       else
        !          1924:        {
        !          1925:          if (live_regs_mask || regs_ever_live[14])
        !          1926:            {
        !          1927:              live_regs_mask |= 0x4000;
        !          1928:              print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE);
        !          1929:              code_size += 4;
        !          1930:            }
        !          1931:          if (current_function_pretend_args_size)
        !          1932:            {
        !          1933:              operands[0] = operands[1] = stack_pointer_rtx;
        !          1934:              operands[2] = gen_rtx (CONST_INT, VOIDmode,
        !          1935:                                     current_function_pretend_args_size);
        !          1936:              output_add_immediate (operands);
        !          1937:            }
        !          1938:          fputs (TARGET_6 ? "\tmov\tpc, lr\n" : "\tmovs\tpc, lr\n", f);
        !          1939:          code_size += 4;
        !          1940:        }
        !          1941:     }
        !          1942:   arm_increase_location (code_size);
        !          1943:   current_function_anonymous_args = 0;
        !          1944: } /* output_epilogue */
        !          1945: 
        !          1946: /* Increase the `arm_text_location' by AMOUNT if we're in the text
        !          1947:    segment.  */
        !          1948: 
        !          1949: void
        !          1950: arm_increase_location (amount)
        !          1951:      int amount;
        !          1952: {
        !          1953:   if (in_text_section ())
        !          1954:     arm_text_location += amount;
        !          1955: } /* arm_increase_location */
        !          1956: 
        !          1957: 
        !          1958: /* Like output_asm_insn (), but also increases the arm_text_location (if in
        !          1959:    the .text segment, of course, even though this will always be true).
        !          1960:    Returns the empty string.  */
        !          1961: 
        !          1962: char *
        !          1963: arm_output_asm_insn (template, operands)
        !          1964:      char *template;
        !          1965:      rtx *operands;
        !          1966: {
        !          1967:   extern FILE *asm_out_file;
        !          1968: 
        !          1969:   output_asm_insn (template, operands);
        !          1970:   if (in_text_section ())
        !          1971:     arm_text_location += 4;
        !          1972:   fflush (asm_out_file);
        !          1973:   return ("");
        !          1974: } /* arm_output_asm_insn */
        !          1975: 
        !          1976: 
        !          1977: /* Output a label definition.  If this label is within the .text segment, it
        !          1978:    is stored in OFFSET_TABLE, to be used when building `llc' instructions.
        !          1979:    Maybe GCC remembers names not starting with a `*' for a long time, but this
        !          1980:    is a minority anyway, so we just make a copy.  Do not store the leading `*'
        !          1981:    if the name starts with one.  */
        !          1982: 
        !          1983: void
        !          1984: arm_asm_output_label (stream, name)
        !          1985:      FILE *stream;
        !          1986:      char *name;
        !          1987: {
        !          1988:   char *real_name, *s;
        !          1989:   struct label_offset *cur;
        !          1990:   int hash = 0;
        !          1991: 
        !          1992:   assemble_name (stream, name);
        !          1993:   fputs (":\n", stream);
        !          1994:   if (! in_text_section ())
        !          1995:     return;
        !          1996: 
        !          1997:   if (name[0] == '*')
        !          1998:     {
        !          1999:       real_name = xmalloc (1 + strlen (&name[1]));
        !          2000:       strcpy (real_name, &name[1]);
        !          2001:     }
        !          2002:   else
        !          2003:     {
        !          2004:       real_name = xmalloc (2 + strlen (name));
        !          2005:       strcpy (real_name, "_");
        !          2006:       strcat (real_name, name);
        !          2007:     }
        !          2008:   for (s = real_name; *s; s++)
        !          2009:     hash += *s;
        !          2010:   hash = hash % LABEL_HASH_SIZE;
        !          2011:   cur = (struct label_offset *) xmalloc (sizeof (struct label_offset));
        !          2012:   cur->name = real_name;
        !          2013:   cur->offset = arm_text_location;
        !          2014:   cur->cdr = offset_table[hash];
        !          2015:   offset_table[hash] = cur;
        !          2016: } /* arm_asm_output_label */
        !          2017: 
        !          2018: 
        !          2019: /* Output the instructions needed to perform what Martin's /bin/as called
        !          2020:    llc: load an SImode thing from the function's constant pool.
        !          2021: 
        !          2022:    XXX This could be enhanced in that we do not really need a pointer in the
        !          2023:    constant pool pointing to the real thing.  If we can address this pointer,
        !          2024:    we can also address what it is pointing at, in fact, anything in the text
        !          2025:    segment which has been defined already within this .s file.  */
        !          2026: 
        !          2027: char *
        !          2028: arm_output_llc (operands)
        !          2029:      rtx *operands;
        !          2030: {
        !          2031:   char *s, *name = XSTR (XEXP (operands[1], 0), 0);
        !          2032:   struct label_offset *he;
        !          2033:   int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4);
        !          2034: 
        !          2035:   if (*name != '*')
        !          2036:     abort ();
        !          2037: 
        !          2038:   for (s = &name[1]; *s; s++)
        !          2039:     hash += *s;
        !          2040:   hash = hash % LABEL_HASH_SIZE;
        !          2041:   he = offset_table[hash];
        !          2042:   while (he && strcmp (he->name, &name[1]))
        !          2043:     he = he->cdr;
        !          2044: 
        !          2045:   if (!he)
        !          2046:     abort ();
        !          2047: 
        !          2048:   if (arm_text_location + 8 - he->offset < 4095)
        !          2049:     {
        !          2050:       fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n",
        !          2051:               conditional ? arm_condition_codes[arm_current_cc] : "",
        !          2052:               reg_names[REGNO (operands[0])], &name[1]);
        !          2053:       arm_increase_location (4);
        !          2054:       return ("");
        !          2055:     }
        !          2056:   else
        !          2057:     {
        !          2058:       int offset = - (arm_text_location + 8 - he->offset);
        !          2059:       char *reg_name = reg_names[REGNO (operands[0])];
        !          2060: 
        !          2061:       /* ??? This is a hack, assuming the constant pool never is more than
        !          2062:         (1 + 255) * 4096 == 1Meg away from the PC.  */
        !          2063: 
        !          2064:       if (offset > 1000000)
        !          2065:        abort ();
        !          2066: 
        !          2067:       fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n",
        !          2068:               conditional ? arm_condition_codes[arm_current_cc] : "",
        !          2069:               reg_name, &name[1]);
        !          2070:       fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n",
        !          2071:               conditional ? arm_condition_codes[arm_current_cc] : "",
        !          2072:               reg_name, reg_name, &name[1]);
        !          2073:       arm_increase_location (8);
        !          2074:     }
        !          2075:   return ("");
        !          2076: } /* arm_output_llc */
        !          2077: 
        !          2078: /* output_load_symbol ()
        !          2079:    load a symbol that is known to be in the text segment into a register */
        !          2080: 
        !          2081: char *
        !          2082: output_load_symbol (operands)
        !          2083: rtx *operands;
        !          2084: {
        !          2085:   char *s, *name = XSTR (operands[1], 0);
        !          2086:   struct label_offset *he;
        !          2087:   int hash = 0;
        !          2088:   int offset;
        !          2089:   
        !          2090:   if (*name != '*')
        !          2091:     abort ();
        !          2092: 
        !          2093:   for (s = &name[1]; *s; s++)
        !          2094:     hash += *s;
        !          2095:   hash = hash % LABEL_HASH_SIZE;
        !          2096:   he = offset_table[hash];
        !          2097:   while (he && strcmp (he->name, &name[1]))
        !          2098:     he = he->cdr;
        !          2099:   
        !          2100:   if (!he)
        !          2101:     abort ();
        !          2102:   
        !          2103:   offset = (arm_text_location + 8 - he->offset);
        !          2104:   if (offset < 0)
        !          2105:     abort ();
        !          2106: 
        !          2107:   /* If the symbol is word aligned then we might be able to reduce the
        !          2108:      number of loads */
        !          2109:   if ((offset & 3) == 0)
        !          2110:     {
        !          2111:       arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 1023", operands);
        !          2112:       if (offset > 0x3ff)
        !          2113:         {
        !          2114:          arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 261120",
        !          2115:                               operands);
        !          2116:          if (offset > 0x3ffff)
        !          2117:            {
        !          2118:              arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 66846720",
        !          2119:                                   operands);
        !          2120:              if (offset > 0x3ffffff)
        !          2121:                arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -67108864",
        !          2122:                                       operands);
        !          2123:            }
        !          2124:         }
        !          2125:     }
        !          2126:   else
        !          2127:     {
        !          2128:       arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 255", operands);
        !          2129:       if (offset > 0x0ff)
        !          2130:         {
        !          2131:          arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 65280", operands);
        !          2132:          if (offset > 0x0ffff)
        !          2133:            {
        !          2134:              arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 16711680",
        !          2135:                                   operands);
        !          2136:              if (offset > 0x0ffffff)
        !          2137:                arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -16777216",
        !          2138:                                     operands);
        !          2139:            }
        !          2140:         }
        !          2141:     }
        !          2142:   return "";
        !          2143: }
        !          2144: 
        !          2145: /* Output code resembling an .lcomm directive.  /bin/as doesn't have this
        !          2146:    directive hence this hack, which works by reserving some `.space' in the
        !          2147:    bss segment directly.
        !          2148: 
        !          2149:    XXX This is a severe hack, which is guaranteed NOT to work since it doesn't
        !          2150:    define STATIC COMMON space but merely STATIC BSS space.  */
        !          2151: 
        !          2152: void
        !          2153: output_lcomm_directive (stream, name, size, rounded)
        !          2154:      FILE *stream;
        !          2155:      char *name;
        !          2156:      int size, rounded;
        !          2157: {
        !          2158:   fputs ("\n\t.bss\t@ .lcomm\n", stream);
        !          2159:   assemble_name (stream, name);
        !          2160:   fprintf (stream, ":\t.space\t%d\n", rounded);
        !          2161:   if (in_text_section ())
        !          2162:     fputs ("\n\t.text\n", stream);
        !          2163:   else
        !          2164:     fputs ("\n\t.data\n", stream);
        !          2165: } /* output_lcomm_directive */
        !          2166: 
        !          2167: /* A finite state machine takes care of noticing whether or not instructions
        !          2168:    can be conditionally executed, and thus decrease execution time and code
        !          2169:    size by deleting branch instructions.  The fsm is controlled by
        !          2170:    final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE.  */
        !          2171: 
        !          2172: /* The state of the fsm controlling condition codes are:
        !          2173:    0: normal, do nothing special
        !          2174:    1: make ASM_OUTPUT_OPCODE not output this instruction
        !          2175:    2: make ASM_OUTPUT_OPCODE not output this instruction
        !          2176:    3: make instructions conditional
        !          2177:    4: make instructions conditional
        !          2178: 
        !          2179:    State transitions (state->state by whom under condition):
        !          2180:    0 -> 1 final_prescan_insn if the `target' is a label
        !          2181:    0 -> 2 final_prescan_insn if the `target' is an unconditional branch
        !          2182:    1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch
        !          2183:    2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch
        !          2184:    3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached
        !          2185:           (the target label has CODE_LABEL_NUMBER equal to arm_target_label).
        !          2186:    4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
        !          2187:           (the target insn is arm_target_insn).
        !          2188: 
        !          2189:    If the jump clobbers the conditions then we use states 2 and 4.
        !          2190: 
        !          2191:    A similar thing can be done with conditional return insns.
        !          2192: 
        !          2193:    XXX In case the `target' is an unconditional branch, this conditionalising
        !          2194:    of the instructions always reduces code size, but not always execution
        !          2195:    time.  But then, I want to reduce the code size to somewhere near what
        !          2196:    /bin/cc produces.  */
        !          2197: 
        !          2198: /* The condition codes of the ARM, and the inverse function.  */
        !          2199: char *arm_condition_codes[] =
        !          2200: {
        !          2201:   "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
        !          2202:   "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
        !          2203: };
        !          2204: 
        !          2205: #define ARM_INVERSE_CONDITION_CODE(X)  ((X) ^ 1)
        !          2206: 
        !          2207: /* Returns the index of the ARM condition code string in
        !          2208:    `arm_condition_codes'.  COMPARISON should be an rtx like
        !          2209:    `(eq (...) (...))'.  */
        !          2210: 
        !          2211: int
        !          2212: get_arm_condition_code (comparison)
        !          2213:      rtx comparison;
        !          2214: {
        !          2215:   switch (GET_CODE (comparison))
        !          2216:     {
        !          2217:     case NE: return (1);
        !          2218:     case EQ: return (0);
        !          2219:     case GE: return (10);
        !          2220:     case GT: return (12);
        !          2221:     case LE: return (13);
        !          2222:     case LT: return (11);
        !          2223:     case GEU: return (2);
        !          2224:     case GTU: return (8);
        !          2225:     case LEU: return (9);
        !          2226:     case LTU: return (3);
        !          2227:     default: abort ();
        !          2228:     }
        !          2229:   /*NOTREACHED*/
        !          2230:   return (42);
        !          2231: } /* get_arm_condition_code */
        !          2232: 
        !          2233: 
        !          2234: void
        !          2235: final_prescan_insn (insn, opvec, noperands)
        !          2236:      rtx insn;
        !          2237:      rtx *opvec;
        !          2238:      int noperands;
        !          2239: {
        !          2240:   /* BODY will hold the body of INSN.  */
        !          2241:   register rtx body = PATTERN (insn);
        !          2242: 
        !          2243:   /* This will be 1 if trying to repeat the trick, and things need to be
        !          2244:      reversed if it appears to fail.  */
        !          2245:   int reverse = 0;
        !          2246: 
        !          2247:   /* JUMP_CLOBBERS will be one implies that the conditions if a branch is
        !          2248:      taken are clobbered, even if the rtl suggests otherwise.  It also
        !          2249:      means that we have to grub around within the jump expression to find
        !          2250:      out what the conditions are when the jump isn't taken.  */
        !          2251:   int jump_clobbers = 0;
        !          2252:   
        !          2253:   /* If we start with a return insn, we only succeed if we find another one. */
        !          2254:   int seeking_return = 0;
        !          2255:   
        !          2256:   /* START_INSN will hold the insn from where we start looking.  This is the
        !          2257:      first insn after the following code_label if REVERSE is true.  */
        !          2258:   rtx start_insn = insn;
        !          2259: 
        !          2260:   /* If in state 4, check if the target branch is reached, in order to
        !          2261:      change back to state 0.  */
        !          2262:   if (arm_ccfsm_state == 4)
        !          2263:     {
        !          2264:       if (insn == arm_target_insn)
        !          2265:       {
        !          2266:        arm_target_insn = NULL;
        !          2267:        arm_ccfsm_state = 0;
        !          2268:       }
        !          2269:       return;
        !          2270:     }
        !          2271: 
        !          2272:   /* If in state 3, it is possible to repeat the trick, if this insn is an
        !          2273:      unconditional branch to a label, and immediately following this branch
        !          2274:      is the previous target label which is only used once, and the label this
        !          2275:      branch jumps to is not too far off.  */
        !          2276:   if (arm_ccfsm_state == 3)
        !          2277:     {
        !          2278:       if (simplejump_p (insn))
        !          2279:        {
        !          2280:          start_insn = next_nonnote_insn (start_insn);
        !          2281:          if (GET_CODE (start_insn) == BARRIER)
        !          2282:            {
        !          2283:              /* XXX Isn't this always a barrier?  */
        !          2284:              start_insn = next_nonnote_insn (start_insn);
        !          2285:            }
        !          2286:          if (GET_CODE (start_insn) == CODE_LABEL
        !          2287:              && CODE_LABEL_NUMBER (start_insn) == arm_target_label
        !          2288:              && LABEL_NUSES (start_insn) == 1)
        !          2289:            reverse = TRUE;
        !          2290:          else
        !          2291:            return;
        !          2292:        }
        !          2293:       else if (GET_CODE (body) == RETURN)
        !          2294:         {
        !          2295:          start_insn = next_nonnote_insn (start_insn);
        !          2296:          if (GET_CODE (start_insn) == BARRIER)
        !          2297:            start_insn = next_nonnote_insn (start_insn);
        !          2298:          if (GET_CODE (start_insn) == CODE_LABEL
        !          2299:              && CODE_LABEL_NUMBER (start_insn) == arm_target_label
        !          2300:              && LABEL_NUSES (start_insn) == 1)
        !          2301:            {
        !          2302:              reverse = TRUE;
        !          2303:              seeking_return = 1;
        !          2304:            }
        !          2305:          else
        !          2306:            return;
        !          2307:         }
        !          2308:       else
        !          2309:        return;
        !          2310:     }
        !          2311: 
        !          2312:   if (arm_ccfsm_state != 0 && !reverse)
        !          2313:     abort ();
        !          2314:   if (GET_CODE (insn) != JUMP_INSN)
        !          2315:     return;
        !          2316: 
        !          2317:   /* This jump might be paralled with a clobber of the condition codes 
        !          2318:      the jump should always come first */
        !          2319:   if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
        !          2320:     body = XVECEXP (body, 0, 0);
        !          2321: 
        !          2322: #if 0  
        !          2323:   /* If this is a conditional return then we don't want to know */
        !          2324:   if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
        !          2325:       && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
        !          2326:       && (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN
        !          2327:           || GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN))
        !          2328:     return;
        !          2329: #endif
        !          2330: 
        !          2331:   if (reverse
        !          2332:       || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
        !          2333:          && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
        !          2334:     {
        !          2335:       int insns_skipped = 0, fail = FALSE, succeed = FALSE;
        !          2336:       /* Flag which part of the IF_THEN_ELSE is the LABEL_REF.  */
        !          2337:       int then_not_else = TRUE;
        !          2338:       rtx this_insn = start_insn, label = 0;
        !          2339: 
        !          2340:       if (get_attr_conds (insn) == CONDS_JUMP_CLOB)
        !          2341:        jump_clobbers = 1;
        !          2342:       
        !          2343:       /* Register the insn jumped to.  */
        !          2344:       if (reverse)
        !          2345:         {
        !          2346:          if (!seeking_return)
        !          2347:            label = XEXP (SET_SRC (body), 0);
        !          2348:         }
        !          2349:       else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
        !          2350:        label = XEXP (XEXP (SET_SRC (body), 1), 0);
        !          2351:       else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
        !          2352:        {
        !          2353:          label = XEXP (XEXP (SET_SRC (body), 2), 0);
        !          2354:          then_not_else = FALSE;
        !          2355:        }
        !          2356:       else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
        !          2357:        seeking_return = 1;
        !          2358:       else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
        !          2359:         {
        !          2360:          seeking_return = 1;
        !          2361:          then_not_else = FALSE;
        !          2362:         }
        !          2363:       else
        !          2364:        abort ();
        !          2365: 
        !          2366:       /* See how many insns this branch skips, and what kind of insns.  If all
        !          2367:         insns are okay, and the label or unconditional branch to the same
        !          2368:         label is not too far away, succeed.  */
        !          2369:       for (insns_skipped = 0;
        !          2370:           !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
        !          2371:           insns_skipped++)
        !          2372:        {
        !          2373:          rtx scanbody;
        !          2374: 
        !          2375:          this_insn = next_nonnote_insn (this_insn);
        !          2376:          if (!this_insn)
        !          2377:            break;
        !          2378: 
        !          2379:          scanbody = PATTERN (this_insn);
        !          2380: 
        !          2381:          switch (GET_CODE (this_insn))
        !          2382:            {
        !          2383:            case CODE_LABEL:
        !          2384:              /* Succeed if it is the target label, otherwise fail since
        !          2385:                 control falls in from somewhere else.  */
        !          2386:              if (this_insn == label)
        !          2387:                {
        !          2388:                  if (jump_clobbers)
        !          2389:                    {
        !          2390:                      arm_ccfsm_state = 2;
        !          2391:                      this_insn = next_nonnote_insn (this_insn);
        !          2392:                    }
        !          2393:                  else
        !          2394:                    arm_ccfsm_state = 1;
        !          2395:                  succeed = TRUE;
        !          2396:                }
        !          2397:              else
        !          2398:                fail = TRUE;
        !          2399:              break;
        !          2400: 
        !          2401:            case BARRIER:
        !          2402:              /* Succeed if the following insn is the target label.
        !          2403:                 Otherwise fail.  
        !          2404:                 If return insns are used then the last insn in a function 
        !          2405:                 will be a barrier. */
        !          2406:              this_insn = next_nonnote_insn (this_insn);
        !          2407:              if (this_insn && this_insn == label)
        !          2408:                {
        !          2409:                  if (jump_clobbers)
        !          2410:                    {
        !          2411:                      arm_ccfsm_state = 2;
        !          2412:                      this_insn = next_nonnote_insn (this_insn);
        !          2413:                    }
        !          2414:                  else
        !          2415:                    arm_ccfsm_state = 1;
        !          2416:                  succeed = TRUE;
        !          2417:                }
        !          2418:              else
        !          2419:                fail = TRUE;
        !          2420:              break;
        !          2421: 
        !          2422:            case CALL_INSN:
        !          2423:              /* The arm 6xx uses full 32 bit addresses so the cc is not 
        !          2424:                 preserved over calls */
        !          2425:              if (TARGET_6)
        !          2426:                fail = TRUE;
        !          2427:              break;
        !          2428:            case JUMP_INSN:
        !          2429:              /* If this is an unconditional branch to the same label, succeed.
        !          2430:                 If it is to another label, do nothing.  If it is conditional,
        !          2431:                 fail.  */
        !          2432:              /* XXX Probably, the test for the SET and the PC are unnecessary. */
        !          2433: 
        !          2434:              if (GET_CODE (scanbody) == SET
        !          2435:                  && GET_CODE (SET_DEST (scanbody)) == PC)
        !          2436:                {
        !          2437:                  if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
        !          2438:                      && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
        !          2439:                    {
        !          2440:                      arm_ccfsm_state = 2;
        !          2441:                      succeed = TRUE;
        !          2442:                    }
        !          2443:                  else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
        !          2444:                    fail = TRUE;
        !          2445:                }
        !          2446:              else if (GET_CODE (scanbody) == RETURN
        !          2447:                       && seeking_return)
        !          2448:                {
        !          2449:                  arm_ccfsm_state = 2;
        !          2450:                  succeed = TRUE;
        !          2451:                }
        !          2452:              else if (GET_CODE (scanbody) == PARALLEL)
        !          2453:                {
        !          2454:                  switch (get_attr_conds (this_insn))
        !          2455:                    {
        !          2456:                    case CONDS_NOCOND:
        !          2457:                      break;
        !          2458:                    default:
        !          2459:                      fail = TRUE;
        !          2460:                      break;
        !          2461:                    }
        !          2462:                }
        !          2463:              break;
        !          2464: 
        !          2465:            case INSN:
        !          2466:              /* Instructions using or affecting the condition codes make it
        !          2467:                 fail.  */
        !          2468:              if ((GET_CODE (scanbody) == SET
        !          2469:                   || GET_CODE (scanbody) == PARALLEL)
        !          2470:                  && get_attr_conds (this_insn) != CONDS_NOCOND)
        !          2471:                fail = TRUE;
        !          2472:              break;
        !          2473: 
        !          2474:            default:
        !          2475:              break;
        !          2476:            }
        !          2477:        }
        !          2478:       if (succeed)
        !          2479:        {
        !          2480:          if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse))
        !          2481:            arm_target_label = CODE_LABEL_NUMBER (label);
        !          2482:          else if (seeking_return || arm_ccfsm_state == 2)
        !          2483:            {
        !          2484:              while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
        !          2485:                {
        !          2486:                  this_insn = next_nonnote_insn (this_insn);
        !          2487:                  if (this_insn && (GET_CODE (this_insn) == BARRIER
        !          2488:                                    || GET_CODE (this_insn) == CODE_LABEL))
        !          2489:                    abort ();
        !          2490:                }
        !          2491:              if (!this_insn)
        !          2492:                {
        !          2493:                  /* Oh, dear! we ran off the end.. give up */
        !          2494:                  recog (PATTERN (insn), insn, NULL_PTR);
        !          2495:                  arm_ccfsm_state = 0;
        !          2496:                  arm_target_insn = NULL;
        !          2497:                  return;
        !          2498:                }
        !          2499:              arm_target_insn = this_insn;
        !          2500:            }
        !          2501:          else
        !          2502:            abort ();
        !          2503:          if (jump_clobbers)
        !          2504:            {
        !          2505:              if (reverse)
        !          2506:                abort ();
        !          2507:              arm_current_cc = 
        !          2508:                  get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body),
        !          2509:                                                            0), 0), 1));
        !          2510:              if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND)
        !          2511:                arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
        !          2512:              if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE)
        !          2513:                arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
        !          2514:            }
        !          2515:          else
        !          2516:            {
        !          2517:              /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
        !          2518:                 what it was.  */
        !          2519:              if (!reverse)
        !          2520:                arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body),
        !          2521:                                                               0));
        !          2522:            }
        !          2523: 
        !          2524:          if (reverse || then_not_else)
        !          2525:            arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
        !          2526:        }
        !          2527:       /* restore recog_operand (getting the attributes of other insns can
        !          2528:         destroy this array, but final.c assumes that it remains intact
        !          2529:         accross this call; since the insn has been recognized already we
        !          2530:         call recog direct). */
        !          2531:       recog (PATTERN (insn), insn, NULL_PTR);
        !          2532:     }
        !          2533: } /* final_prescan_insn */
        !          2534: 
        !          2535: /* EOF */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.