Annotation of gcc/config/arm/arm.c, revision 1.1.1.1

1.1       root        1: /* Output routines for GCC for ARM/RISCiX.
                      2:    Copyright (C) 1991 Free Software Foundation, Inc.
                      3:    Contributed by Pieter `Tiggr' Schoenmakers ([email protected])
                      4:              and Martin Simmons (@harleqn.co.uk).
                      5: 
                      6: This file is part of GNU CC.
                      7: 
                      8: GNU CC is free software; you can redistribute it and/or modify
                      9: it under the terms of the GNU General Public License as published by
                     10: the Free Software Foundation; either version 2, or (at your option)
                     11: any later version.
                     12: 
                     13: GNU CC is distributed in the hope that it will be useful,
                     14: but WITHOUT ANY WARRANTY; without even the implied warranty of
                     15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     16: GNU General Public License for more details.
                     17: 
                     18: You should have received a copy of the GNU General Public License
                     19: along with GNU CC; see the file COPYING.  If not, write to
                     20: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
                     21: 
                     22: #include <stdio.h>
                     23: #include "assert.h"
                     24: #include "config.h"
                     25: #include "rtl.h"
                     26: #include "regs.h"
                     27: #include "hard-reg-set.h"
                     28: #include "real.h"
                     29: #include "insn-config.h"
                     30: #include "conditions.h"
                     31: #include "insn-flags.h"
                     32: #include "output.h"
                     33: #include "insn-attr.h"
                     34: #include "flags.h"
                     35: 
                     36: /* The maximum number of insns skipped which will be conditionalised if
                     37:    possible.  */
                     38: #define MAX_INSNS_SKIPPED  5
                     39: 
                     40: /* Some function declarations.  */
                     41: extern FILE *asm_out_file;
                     42: extern char *output_multi_immediate ();
                     43: extern char *arm_output_asm_insn ();
                     44: extern void arm_increase_location ();
                     45: 
                     46: /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
                     47:    must report the mode of the memory reference from PRINT_OPERAND to
                     48:    PRINT_OPERAND_ADDRESS.  */
                     49: int output_memory_reference_mode;
                     50: 
                     51: /* Nonzero if the prologue must setup `fp'.  */
                     52: int current_function_anonymous_args;
                     53: 
                     54: /* Location counter of .text segment.  */
                     55: int arm_text_location = 0;
                     56: 
                     57: /* A hash table is used to store text segment labels and their associated
                     58:    offset from the start of the text segment.  */
                     59: struct label_offset
                     60: {
                     61:   char *name;
                     62:   int offset;
                     63:   struct label_offset *cdr;
                     64: };
                     65: 
                     66: #define LABEL_HASH_SIZE  257
                     67: 
                     68: static struct label_offset *offset_table[LABEL_HASH_SIZE];
                     69: 
                     70: /* For an explanation of these variables, see final_prescan_insn below.  */
                     71: int arm_ccfsm_state;
                     72: int arm_current_cc;
                     73: rtx arm_target_insn;
                     74: int arm_target_label;
                     75: char *arm_condition_codes[];
                     76: 
                     77: /* Return the number of mov instructions needed to get the constant VALUE into
                     78:    a register.  */
                     79: 
                     80: int
                     81: arm_const_nmoves (value)
                     82:      register int value;
                     83: {
                     84:   register int i;
                     85: 
                     86:   if (value == 0)
                     87:     return (1);
                     88:   for (i = 0; value; i++, value &= ~0xff)
                     89:     while ((value & 3) == 0)
                     90:       value = (value >> 2) | ((value & 3) << 30);
                     91:   return (i);
                     92: } /* arm_const_nmoves */
                     93: 
                     94: 
                     95: /* Return TRUE if int I is a valid immediate ARM constant.  */
                     96: 
                     97: int
                     98: const_ok_for_arm (i)
                     99:      int i;
                    100: {
                    101:   unsigned int mask = ~0xFF;
                    102: 
                    103:   do
                    104:     {
                    105:       if ((i & mask) == 0)
                    106:        return(TRUE);
                    107:       mask = (mask << 2) | (mask >> (32 - 2));
                    108:     } while (mask != ~0xFF);
                    109: 
                    110:   return (FALSE);
                    111: } /* const_ok_for_arm */
                    112: 
                    113: /* Return TRUE if rtx X is a valid immediate FPU constant. */
                    114: 
                    115: int
                    116: const_double_rtx_ok_for_fpu (x)
                    117:      rtx x;
                    118: {
                    119:   double d;
                    120:   union real_extract u;
                    121:   u.i[0] = CONST_DOUBLE_LOW(x);
                    122:   u.i[1] = CONST_DOUBLE_HIGH(x);
                    123:   d = u.d;
                    124: 
                    125:   return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0
                    126:          || d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);
                    127: } /* const_double_rtx_ok_for_fpu */
                    128: 
                    129: /* Predicates for `match_operand' and `match_operator'.  */
                    130: 
                    131: /* Return TRUE for valid operands for the rhs of an ARM instruction.  */
                    132: 
                    133: int
                    134: arm_rhs_operand (op, mode)
                    135:      rtx op;
                    136:      enum machine_mode mode;
                    137: {
                    138:   return (register_operand (op, mode)
                    139:          || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
                    140: } /* arm_rhs_operand */
                    141: 
                    142: /* Return TRUE for valid operands for the rhs of an FPU instruction.  */
                    143: 
                    144: int
                    145: fpu_rhs_operand (op, mode)
                    146:      rtx op;
                    147:      enum machine_mode mode;
                    148: {
                    149:   if (register_operand (op, mode))
                    150:     return(TRUE);
                    151:   else if (GET_CODE (op) == CONST_DOUBLE)
                    152:     return (const_double_rtx_ok_for_fpu (op));
                    153:   else return (FALSE);
                    154: } /* fpu_rhs_operand */
                    155: 
                    156: /* Return nonzero if OP is a constant power of two.  */
                    157: 
                    158: int
                    159: power_of_two_operand (op, mode)
                    160:      rtx op;
                    161:      enum machine_mode mode;
                    162: {
                    163:   if (GET_CODE (op) == CONST_INT)
                    164:     {
                    165:       int value = INTVAL(op);
                    166:       return (value != 0  &&  (value & (value-1)) == 0);
                    167:     }
                    168:   return (FALSE);
                    169: } /* power_of_two_operand */
                    170: 
                    171: /* Return TRUE for a valid operand of a DImode operation.
                    172:    Either: REG, CONST_DOUBLE or MEM(offsettable).
                    173:    Note that this disallows MEM(REG+REG).  */
                    174: 
                    175: int
                    176: di_operand (op, mode)
                    177:      rtx op;
                    178:      enum machine_mode mode;
                    179: {
                    180:   if (register_operand (op, mode))
                    181:     return (TRUE);
                    182: 
                    183:   switch (GET_CODE (op))
                    184:     {
                    185:     case CONST_DOUBLE:
                    186:     case CONST_INT:
                    187:       return (TRUE);
                    188:     case MEM:
                    189:       return (memory_address_p (DImode, XEXP (op, 0))
                    190:              && offsettable_address_p (FALSE, DImode, XEXP (op, 0)));
                    191:     default:
                    192:       return (FALSE);
                    193:     }
                    194: } /* di_operand */
                    195: 
                    196: /* Return TRUE for valid index operands. */
                    197: 
                    198: int
                    199: index_operand (op, mode)
                    200:      rtx op;
                    201:      enum machine_mode mode;
                    202: {
                    203:   return (register_operand(op, mode)
                    204:          || (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));
                    205: } /* index_operand */
                    206: 
                    207: /* Return TRUE for arithmetic operators which can be combined with a multiply
                    208:    (shift).  */
                    209: 
                    210: int
                    211: shiftable_operator (x, mode)
                    212:      rtx x;
                    213:      enum machine_mode mode;
                    214: {
                    215:   if (GET_MODE (x) != mode)
                    216:     return FALSE;
                    217:   else
                    218:     {
                    219:       enum rtx_code code = GET_CODE (x);
                    220: 
                    221:       return (code == PLUS || code == MINUS
                    222:              || code == IOR || code == XOR || code == AND);
                    223:     }
                    224: } /* shiftable_operator */
                    225: 
                    226: /* Return TRUE for shift operators. */
                    227: 
                    228: int
                    229: shift_operator (x, mode)
                    230:      rtx x;
                    231:      enum machine_mode mode;
                    232: {
                    233:   if (GET_MODE (x) != mode)
                    234:     return FALSE;
                    235:   else
                    236:     {
                    237:       enum rtx_code code = GET_CODE (x);
                    238: 
                    239:       return (code == ASHIFT || code == LSHIFT
                    240:              || code == ASHIFTRT || code == LSHIFTRT);
                    241:     }
                    242: } /* shift_operator */
                    243: 
                    244: /* Routines to output assembly language.  */
                    245: 
                    246: /* Output the operands of a LDM/STM instruction to STREAM.
                    247:    MASK is the ARM register set mask of which only bits 0-15 are important.
                    248:    INSTR is the possibly suffixed base register.  HAT unequals zero if a hat
                    249:    must follow the register list.  */
                    250: 
                    251: void
                    252: print_multi_reg (stream, instr, mask, hat)
                    253:      FILE *stream;
                    254:      char *instr;
                    255:      int mask, hat;
                    256: {
                    257:   int i;
                    258:   int not_first = FALSE;
                    259: 
                    260:   fprintf (stream, "\t%s, {", instr);
                    261:   for (i = 0; i < 16; i++)
                    262:     if (mask & (1 << i))
                    263:       {
                    264:        if (not_first)
                    265:          fprintf (stream, ", ");
                    266:        fprintf (stream, "%s", reg_names[i]);
                    267:        not_first = TRUE;
                    268:       }
                    269:   fprintf (stream, "}%s\n", hat ? "^" : "");
                    270: } /* print_multi_reg */
                    271: 
                    272: /* Output a 'call' insn. */
                    273: 
                    274: char *
                    275: output_call (operands)
                    276:        rtx operands[];
                    277: {
                    278:   operands[0] = XEXP (operands[0], 0);
                    279: 
                    280:   /* Handle calls to lr using ip (which may be clobbered in subr anyway). */
                    281: 
                    282:   if (REGNO (operands[0]) == 14)
                    283:     {
                    284:       operands[0] = gen_rtx (REG, SImode, 12);
                    285:       arm_output_asm_insn ("mov\t%0, lr", operands);
                    286:     }
                    287:   arm_output_asm_insn ("mov\tlr, pc", operands);
                    288:   arm_output_asm_insn ("mov\tpc, %0", operands);
                    289:   return ("");
                    290: } /* output_call */
                    291: 
                    292: /* Output a move from arm registers to an fpu registers.
                    293:    OPERANDS[0] is an fpu register.
                    294:    OPERANDS[1] is the first registers of an arm register pair.  */
                    295: 
                    296: char *
                    297: output_mov_double_fpu_from_arm (operands)
                    298:      rtx operands[];
                    299: {
                    300:   int arm_reg0 = REGNO (operands[1]);
                    301:   rtx ops[2];
                    302: 
                    303:   if (arm_reg0 == 12)
                    304:     abort();
                    305:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
                    306:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
                    307:   arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);
                    308:   arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);
                    309:   return ("");
                    310: } /* output_mov_double_fpu_from_arm */
                    311: 
                    312: /* Output a move from an fpu register to arm registers.
                    313:    OPERANDS[0] is the first registers of an arm register pair.
                    314:    OPERANDS[1] is an fpu register.  */
                    315: 
                    316: char *
                    317: output_mov_double_arm_from_fpu (operands)
                    318:      rtx operands[];
                    319: {
                    320:   int arm_reg0 = REGNO (operands[0]);
                    321:   rtx ops[2];
                    322: 
                    323:   if (arm_reg0 == 12)
                    324:     abort();
                    325:   ops[0] = gen_rtx (REG, SImode, arm_reg0);
                    326:   ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
                    327:   arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);
                    328:   arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops);
                    329:   return("");
                    330: } /* output_mov_double_arm_from_fpu */
                    331: 
                    332: /* Output a move between double words.
                    333:    It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM
                    334:    or MEM<-REG and all MEMs must be offsettable addresses.  */
                    335: 
                    336: char *
                    337: output_move_double (operands)
                    338:      rtx operands[];
                    339: {
                    340:   enum rtx_code code0 = GET_CODE (operands[0]);
                    341:   enum rtx_code code1 = GET_CODE (operands[1]);
                    342:   rtx otherops[2];
                    343: 
                    344:   if (code0 == REG)
                    345:     {
                    346:       int reg0 = REGNO (operands[0]);
                    347: 
                    348:       otherops[0] = gen_rtx (REG, SImode, 1 + reg0);
                    349:       if (code1 == REG)
                    350:        {
                    351:          int reg1 = REGNO (operands[1]);
                    352:          if (reg1 == 12)
                    353:            abort();
                    354:          otherops[1] = gen_rtx (REG, SImode, 1 + reg1);
                    355: 
                    356:          /* Ensure the second source is not overwritten */
                    357:          if (reg0 == 1 + reg1)
                    358:            {
                    359:              arm_output_asm_insn("mov\t%0, %1", otherops);
                    360:              arm_output_asm_insn("mov\t%0, %1", operands);
                    361:            }
                    362:          else
                    363:            {
                    364:              arm_output_asm_insn("mov\t%0, %1", operands);
                    365:              arm_output_asm_insn("mov\t%0, %1", otherops);
                    366:            }
                    367:        }
                    368:       else if (code1 == CONST_DOUBLE)
                    369:        {
                    370:          otherops[1] = gen_rtx (CONST_INT, VOIDmode,
                    371:                                 CONST_DOUBLE_HIGH (operands[1]));
                    372:          operands[1] = gen_rtx (CONST_INT, VOIDmode,
                    373:                                 CONST_DOUBLE_LOW (operands[1]));
                    374:          arm_output_asm_insn ("mov\t%0, %1", operands);
                    375:          arm_output_asm_insn ("mov\t%0, %1", otherops);
                    376:        }
                    377:       else if (code1 == CONST_INT)
                    378:        {
                    379:          otherops[1] = const0_rtx;
                    380:          arm_output_asm_insn ("mov\t%0, %1", operands);
                    381:          arm_output_asm_insn ("mov\t%0, %1", otherops);
                    382:        }
                    383:       else if (code1 == MEM)
                    384:        {
                    385:          if (GET_CODE (XEXP (operands[1], 0)) == REG)
                    386:            {
                    387:              /* Handle the simple case where address is [r, #0] more
                    388:                 efficient.  */
                    389:              operands[1] = XEXP (operands[1], 0);
                    390:              arm_output_asm_insn ("ldmia\t%1, %M0", operands);
                    391:            }
                    392:          else
                    393:            {
                    394:              otherops[1] = adj_offsettable_operand (operands[1], 4);
                    395:              /* Take care of overlapping base/data reg.  */
                    396:              if (reg_mentioned_p (operands[0], operands[1]))
                    397:                {
                    398:                  arm_output_asm_insn ("ldr\t%0, %1", otherops);
                    399:                  arm_output_asm_insn ("ldr\t%0, %1", operands);
                    400:                }
                    401:              else
                    402:                {
                    403:                  arm_output_asm_insn ("ldr\t%0, %1", operands);
                    404:                  arm_output_asm_insn ("ldr\t%0, %1", otherops);
                    405:                }
                    406:            }
                    407:        }
                    408:       else abort();  /* Constraints should prevent this */
                    409:     }
                    410:   else if (code0 == MEM && code1 == REG)
                    411:     {
                    412:       if (REGNO (operands[1]) == 12)
                    413:        abort();
                    414: 
                    415:       if (GET_CODE (XEXP (operands[0], 0)) == REG)
                    416:        {
                    417:          operands[0] = XEXP (operands[0], 0);
                    418:          arm_output_asm_insn ("stmia\t%0, %M1", operands);
                    419:        }
                    420:       else
                    421:        {
                    422:          otherops[0] = adj_offsettable_operand (operands[0], 4);
                    423:          otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
                    424:          arm_output_asm_insn ("str\t%1, %0", operands);
                    425:          arm_output_asm_insn ("str\t%1, %0", otherops);
                    426:        }
                    427:     }
                    428:   else abort();  /* Constraints should prevent this */
                    429: 
                    430:   return("");
                    431: } /* output_move_double */
                    432: 
                    433: 
                    434: /* Output an arbitrary MOV reg, #n.
                    435:    OPERANDS[0] is a register.  OPERANDS[1] is a const_int.  */
                    436: 
                    437: char *
                    438: output_mov_immediate (operands)
                    439:      rtx operands[2];
                    440: {
                    441:   int n = INTVAL (operands[1]);
                    442:   int n_ones = 0;
                    443:   int i;
                    444: 
                    445:   /* Try to use one MOV */
                    446: 
                    447:   if (const_ok_for_arm (n))
                    448:     return (arm_output_asm_insn ("mov\t%0, %1", operands));
                    449: 
                    450:   /* Try to use one MVN */
                    451: 
                    452:   if (const_ok_for_arm(~n))
                    453:     {
                    454:       operands[1] = gen_rtx (CONST_INT, VOIDmode, ~n);
                    455:       return (arm_output_asm_insn ("mvn\t%0, %1", operands));
                    456:     }
                    457: 
                    458:   /* If all else fails, make it out of ORRs or BICs as appropriate. */
                    459: 
                    460:   for (i=0; i < 32; i++)
                    461:     if (n & 1 << i)
                    462:       n_ones++;
                    463: 
                    464:   if (n_ones > 16)  /* Shorter to use MVN with BIC in this case. */
                    465:     output_multi_immediate(operands, "mvn\t%0, %1", "bic\t%0, %0, %1", 1, ~n);
                    466:   else
                    467:     output_multi_immediate(operands, "mov\t%0, %1", "orr\t%0, %0, %1", 1, n);
                    468:   return("");
                    469: } /* output_mov_immediate */
                    470: 
                    471: 
                    472: /* Output an ADD r, s, #n where n may be too big for one instruction.  If
                    473:    adding zero to one register, output nothing.  */
                    474: 
                    475: char *
                    476: output_add_immediate (operands)
                    477:      rtx operands[3];
                    478: {
                    479:   int n = INTVAL (operands[2]);
                    480: 
                    481:   if (n != 0 || REGNO (operands[0]) != REGNO (operands[1]))
                    482:     {
                    483:       if (n < 0)
                    484:        output_multi_immediate (operands,
                    485:                                "sub\t%0, %1, %2", "sub\t%0, %0, %2", 2, -n);
                    486:       else
                    487:        output_multi_immediate (operands,
                    488:                                "add\t%0, %1, %2", "add\t%0, %0, %2", 2, n);
                    489:     }
                    490:   return("");
                    491: } /* output_add_immediate */
                    492: 
                    493: 
                    494: /* Output a multiple immediate operation.
                    495:    OPERANDS is the vector of operands referred to in the output patterns.
                    496:    INSTR1 is the output pattern to use for the first constant.
                    497:    INSTR2 is the output pattern to use for subsequent constants.
                    498:    IMMED_OP is the index of the constant slot in OPERANDS.
                    499:    N is the constant value.  */
                    500: 
                    501: char *
                    502: output_multi_immediate (operands, instr1, instr2, immed_op, n)
                    503:      rtx operands[];
                    504:      char *instr1, *instr2;
                    505:      int immed_op, n;
                    506: {
                    507:   if (n == 0)
                    508:     {
                    509:       operands[immed_op] = const0_rtx;
                    510:       arm_output_asm_insn (instr1, operands); /* Quick and easy output */
                    511:     }
                    512:   else
                    513:     {
                    514:       int i;
                    515:       char *instr = instr1;
                    516: 
                    517:       /* Note that n is never zero here (which would give no output) */
                    518: 
                    519:       for (i = 0; i < 32; i += 2)
                    520:        {
                    521:          if (n & (3 << i))
                    522:            {
                    523:              operands[immed_op] = gen_rtx (CONST_INT, VOIDmode,
                    524:                                            n & (255 << i));
                    525:              arm_output_asm_insn (instr, operands);
                    526:              instr = instr2;
                    527:              i += 6;
                    528:            }
                    529:        }
                    530:     }
                    531:   return ("");
                    532: } /* output_multi_immediate */
                    533: 
                    534: 
                    535: /* Return the appropriate ARM instruction for the operation code.
                    536:    The returned result should not be overwritten.  OP is the rtx of the
                    537:    operation.  SHIFT_FIRST_ARG is TRUE if the first argument of the operator
                    538:    was shifted.  */
                    539: 
                    540: char *
                    541: arithmetic_instr (op, shift_first_arg)
                    542:      rtx op;
                    543: {
                    544:   switch (GET_CODE(op))
                    545:     {
                    546:     case PLUS:
                    547:       return ("add");
                    548:     case MINUS:
                    549:       if (shift_first_arg)
                    550:        return ("rsb");
                    551:       else
                    552:        return ("sub");
                    553:     case IOR:
                    554:       return ("orr");
                    555:     case XOR:
                    556:       return ("eor");
                    557:     case AND:
                    558:       return ("and");
                    559:     default:
                    560:       abort();
                    561:     }
                    562:   return ("");                 /* stupid cc */
                    563: } /* arithmetic_instr */
                    564: 
                    565: 
                    566: /* Ensure valid constant shifts and return the appropriate shift mnemonic
                    567:    for the operation code.  The returned result should not be overwritten.
                    568:    OP is the rtx code of the shift.
                    569:    SHIFT_PTR points to the shift size operand.  */
                    570: 
                    571: char *
                    572: shift_instr (op, shift_ptr)
                    573:      enum rtx_code op;
                    574:      rtx *shift_ptr;
                    575: {
                    576:   int min_shift = 0;
                    577:   int max_shift = 31;
                    578:   char *mnem;
                    579: 
                    580:   switch (op)
                    581:     {
                    582:     case ASHIFT:
                    583:       mnem = "asl";
                    584:       break;
                    585:     case LSHIFT:
                    586:       mnem = "lsl";
                    587:       break;
                    588:     case ASHIFTRT:
                    589:       mnem = "asr";
                    590:       max_shift = 32;
                    591:       break;
                    592:     case LSHIFTRT:
                    593:       mnem = "lsr";
                    594:       max_shift = 32;
                    595:       break;
                    596:     default:
                    597:       abort();
                    598:     }
                    599: 
                    600:   if (GET_CODE (*shift_ptr) == CONST_INT)
                    601:     {
                    602:       int shift = INTVAL (*shift_ptr);
                    603: 
                    604:       if (shift < min_shift)
                    605:        *shift_ptr = gen_rtx (CONST_INT, VOIDmode, 0);
                    606:       else if (shift > max_shift)
                    607:        *shift_ptr = gen_rtx (CONST_INT, VOIDmode, max_shift);
                    608:     }
                    609:   return (mnem);
                    610: } /* shift_instr */
                    611: 
                    612: 
                    613: /* Obtain the shift from the POWER of two. */
                    614: 
                    615: int
                    616: int_log2 (power)
                    617:      unsigned int power;
                    618: {
                    619:   int shift = 0;
                    620: 
                    621:   while (((1 << shift) & power) == 0)
                    622:     {
                    623:       if (shift > 31)
                    624:        abort();
                    625:       shift++;
                    626:     }
                    627:   return (shift);
                    628: } /* int_log2 */
                    629: 
                    630: 
                    631: /* Output an arithmetic instruction which may set the condition code.
                    632:    OPERANDS[0] is the destination register.
                    633:    OPERANDS[1] is the arithmetic operator expression.
                    634:    OPERANDS[2] is the left hand argument.
                    635:    OPERANDS[3] is the right hand argument.
                    636:    CONST_FIRST_ARG is TRUE if the first argument of the operator was constant.
                    637:    SET_COND is TRUE when the condition code should be set.  */
                    638: 
                    639: char *
                    640: output_arithmetic (operands, const_first_arg, set_cond)
                    641:      rtx operands[4];
                    642:      int const_first_arg;
                    643:      int set_cond;
                    644: {
                    645:   char mnemonic[80];
                    646:   char *instr = arithmetic_instr (operands[1], const_first_arg);
                    647: 
                    648:   sprintf (mnemonic, "%s%s\t%%0, %%2, %%3", instr, set_cond ? "s" : "");
                    649:   return (arm_output_asm_insn (mnemonic, operands));
                    650: } /* output_arithmetic */
                    651: 
                    652: 
                    653: /* Output an arithmetic instruction with a shift.
                    654:    OPERANDS[0] is the destination register.
                    655:    OPERANDS[1] is the arithmetic operator expression.
                    656:    OPERANDS[2] is the unshifted register.
                    657:    OPERANDS[3] is the shift operator expression.
                    658:    OPERANDS[4] is the shifted register.
                    659:    OPERANDS[5] is the shift constant or register.
                    660:    SHIFT_FIRST_ARG is TRUE if the first argument of the operator was shifted.
                    661:    SET_COND is TRUE when the condition code should be set.  */
                    662: 
                    663: char *
                    664: output_arithmetic_with_shift (operands, shift_first_arg, set_cond)
                    665:      rtx operands[6];
                    666:      int shift_first_arg;
                    667:      int set_cond;
                    668: {
                    669:   char mnemonic[80];
                    670:   char *instr = arithmetic_instr (operands[1], shift_first_arg);
                    671:   char *condbit = set_cond ? "s" : "";
                    672:   char *shift = shift_instr (GET_CODE (operands[3]), &operands[5]);
                    673: 
                    674:   sprintf (mnemonic, "%s%s\t%%0, %%2, %%4, %s %%5", instr, condbit, shift);
                    675:   return (arm_output_asm_insn (mnemonic, operands));
                    676: } /* output_arithmetic_with_shift */
                    677: 
                    678: 
                    679: /* Output an arithmetic instruction with a power of two multiplication.
                    680:    OPERANDS[0] is the destination register.
                    681:    OPERANDS[1] is the arithmetic operator expression.
                    682:    OPERANDS[2] is the unmultiplied register.
                    683:    OPERANDS[3] is the multiplied register.
                    684:    OPERANDS[4] is the constant multiple (power of two).
                    685:    SHIFT_FIRST_ARG is TRUE if the first arg of the operator was multiplied.  */
                    686: 
                    687: char *
                    688: output_arithmetic_with_immediate_multiply (operands, shift_first_arg)
                    689:      rtx operands[5];
                    690:      int shift_first_arg;
                    691: {
                    692:   char mnemonic[80];
                    693:   char *instr = arithmetic_instr (operands[1], shift_first_arg);
                    694:   int shift = int_log2 (INTVAL (operands[4]));
                    695: 
                    696:   sprintf (mnemonic, "%s\t%%0, %%2, %%3, asl#%d", instr, shift);
                    697:   return (arm_output_asm_insn (mnemonic, operands));
                    698: } /* output_arithmetic_with_immediate_multiply */
                    699: 
                    700: 
                    701: /* Output a move with a shift.
                    702:    OP is the shift rtx code.
                    703:    OPERANDS[0] = destination register.
                    704:    OPERANDS[1] = source register.
                    705:    OPERANDS[2] = shift constant or register.  */
                    706: 
                    707: char *
                    708: output_shifted_move (op, operands)
                    709:      enum rtx_code op;
                    710:      rtx operands[2];
                    711: {
                    712:   char mnemonic[80];
                    713: 
                    714:   if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0)
                    715:     sprintf (mnemonic, "mov\t%%0, %%1");
                    716:   else
                    717:     sprintf (mnemonic, "mov\t%%0, %%1, %s %%2",
                    718:             shift_instr (op, &operands[2]));
                    719:   return (arm_output_asm_insn (mnemonic, operands));
                    720: } /* output_shifted_move */
                    721: 
                    722: 
                    723: /* Output a .ascii pseudo-op, keeping track of lengths.  This is because
                    724:    /bin/as is horribly restrictive.  */
                    725: 
                    726: void
                    727: output_ascii_pseudo_op (stream, p, len)
                    728:      FILE *stream;
                    729:      char *p;
                    730:      int len;
                    731: {
                    732:   int i;
                    733:   int len_so_far = 1000;
                    734:   int chars_so_far = 0;
                    735: 
                    736:   for (i = 0; i < len; i++)
                    737:     {
                    738:       register int c = p[i];
                    739: 
                    740:       if (len_so_far > 50)
                    741:        {
                    742:          if (chars_so_far)
                    743:            fputs ("\"\n", stream);
                    744:          fputs ("\t.ascii\t\"", stream);
                    745:          len_so_far = 0;
                    746:          arm_increase_location (chars_so_far);
                    747:          chars_so_far = 0;
                    748:        }
                    749: 
                    750:       if (c == '\"' || c == '\\')
                    751:        {
                    752:          putc('\\', stream);
                    753:          len_so_far++;
                    754:        }
                    755:       if (c >= ' ' && c < 0177)
                    756:        {
                    757:          putc (c, stream);
                    758:          len_so_far++;
                    759:        }
                    760:       else
                    761:        {
                    762:          fprintf (stream, "\\%03o", c);
                    763:          len_so_far +=4;
                    764:        }
                    765:       chars_so_far++;
                    766:     }
                    767:   fputs ("\"\n", stream);
                    768:   arm_increase_location (chars_so_far);
                    769: } /* output_ascii_pseudo_op */
                    770: 
                    771: void
                    772: output_prologue (f, frame_size)
                    773:      FILE *f;
                    774:      int frame_size;
                    775: {
                    776: 
                    777:   int reg, live_regs_mask = 0, code_size = 0;
                    778:   rtx operands[3];
                    779: 
                    780:   /* Nonzero if the `fp' (argument pointer) register is needed.  */
                    781:   int fp_needed = 0;
                    782: 
                    783:   /* Nonzero if we must stuff some register arguments onto the stack as if
                    784:      they were passed there.  */
                    785:   int store_arg_regs = 0;
                    786: 
                    787:   fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n",
                    788:           current_function_args_size, current_function_pretend_args_size, frame_size);
                    789:   fprintf (f, "\t@ frame_pointer_needed = %d, current_function_anonymous_args = %d\n",
                    790:           frame_pointer_needed, current_function_anonymous_args);
                    791: 
                    792:   if (current_function_pretend_args_size || current_function_args_size
                    793:       || frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
                    794:     fp_needed = 1;
                    795: 
                    796:   if (current_function_anonymous_args && current_function_pretend_args_size)
                    797:     store_arg_regs = 1;
                    798: 
                    799:   for (reg = 4; reg < 10; reg++)
                    800:     if (regs_ever_live[reg])
                    801:       live_regs_mask |= (1 << reg);
                    802: 
                    803:   if (fp_needed)
                    804:     {
                    805:       live_regs_mask |= 0xD800;
                    806:       /* The following statement is probably redundant now
                    807:         because the frame pointer is recorded in regs_ever_live.  */
                    808:       if (frame_pointer_needed)
                    809:        live_regs_mask |= (1 << FRAME_POINTER_REGNUM);
                    810:       fputs ("\tmov\tip, sp\n", f);
                    811:       code_size += 4;
                    812:     }
                    813:   else if (regs_ever_live[14])
                    814:     live_regs_mask |= 0x4000;
                    815: 
                    816:   /* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
                    817:      room.  If also STORE_ARG_REGS store the argument registers involved in
                    818:      the created slot (this is for stdarg and varargs).  */
                    819:   if (current_function_pretend_args_size)
                    820:     {
                    821:       if (store_arg_regs)
                    822:        {
                    823:          int arg_size, mask = 0;
                    824: 
                    825:          assert (current_function_pretend_args_size <= 16);
                    826:          for (reg = 3, arg_size = current_function_pretend_args_size;
                    827:               arg_size > 0; reg--, arg_size -= 4)
                    828:            mask |= (1 << reg);
                    829:          print_multi_reg (f, "stmfd\tsp!", mask, FALSE);
                    830:        }
                    831:       else
                    832:        {
                    833:          operands[0] = operands[1] = stack_pointer_rtx;
                    834:          operands[2] = gen_rtx (CONST_INT, VOIDmode,
                    835:                                 -current_function_pretend_args_size);
                    836:          output_add_immediate (operands);
                    837:        }
                    838:     }
                    839: 
                    840:   if (live_regs_mask)
                    841:     {
                    842:       print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE);
                    843:       code_size += 4;
                    844:     }
                    845: 
                    846:   for (reg = 23; reg > 19; reg--)
                    847:     if (regs_ever_live[reg])
                    848:       {
                    849:        fprintf (f, "\tstfe\t%s, [sp, #-12]!\n", reg_names[reg]);
                    850:        code_size += 4;
                    851:       }
                    852: 
                    853:   if (fp_needed)
                    854:     {
                    855:       /* Make `fp' point to saved value of `pc'. */
                    856: 
                    857:       operands[0] = arg_pointer_rtx;
                    858:       operands[1] = gen_rtx (REG, SImode, 12);
                    859:       operands[2] = gen_rtx (CONST_INT, VOIDmode,
                    860:                             - (4 + current_function_pretend_args_size));
                    861:       output_add_immediate (operands);
                    862:     }
                    863: 
                    864:   if (frame_pointer_needed)
                    865:     {
                    866:       fprintf (f, "\tmov\trfp, sp\n");
                    867:       code_size += 4;
                    868:     }
                    869: 
                    870:   if (frame_size)
                    871:     {
                    872:       operands[0] = operands[1] = stack_pointer_rtx;
                    873:       operands[2] = gen_rtx (CONST_INT, VOIDmode, -frame_size);
                    874:       output_add_immediate (operands);
                    875:     }
                    876: 
                    877:   arm_increase_location (code_size);
                    878: } /* output_prologue */
                    879: 
                    880: 
                    881: void
                    882: output_epilogue (f, frame_size)
                    883:      FILE *f;
                    884:      int frame_size;
                    885: {
                    886:   int reg, live_regs_mask = 0, code_size = 0, fp_needed = 0;
                    887:   rtx operands[3];
                    888: 
                    889:   if (current_function_pretend_args_size || current_function_args_size
                    890:       || frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
                    891:     fp_needed = 1;
                    892: 
                    893:   for (reg = 4; reg < 10; reg++)
                    894:     if (regs_ever_live[reg])
                    895:       live_regs_mask |= (1 << reg);
                    896: 
                    897:   if (fp_needed)
                    898:     {
                    899:       live_regs_mask |= 0xA800;
                    900:       if (frame_pointer_needed)
                    901:         live_regs_mask |= (1 << FRAME_POINTER_REGNUM);
                    902:     }
                    903:   else if (regs_ever_live[14])
                    904:     live_regs_mask |= 0x4000;
                    905: 
                    906:   for (reg = 20; reg < 24; reg++)
                    907:     if (regs_ever_live[reg])
                    908:       {
                    909:        fprintf (f, "\tldfe\t%s, [%s], #12\n", reg_names[reg],
                    910:                 frame_pointer_needed ? "rfp" : "sp");
                    911:        code_size += 4;
                    912:       }
                    913: 
                    914:   if (fp_needed)
                    915:     {
                    916:       print_multi_reg (f, "ldmea\tfp", live_regs_mask, TRUE);
                    917:       code_size += 4;
                    918:     }
                    919:   else
                    920:     {
                    921:       /* Restore stack pointer if necessary.  */
                    922:       if (frame_size)
                    923:        {
                    924:          operands[0] = operands[1] = stack_pointer_rtx;
                    925:          operands[2] = gen_rtx (CONST_INT, VOIDmode, frame_size);
                    926:          output_add_immediate (operands);
                    927:        }
                    928: 
                    929:       if (current_function_pretend_args_size == 0 && regs_ever_live[14])
                    930:        {
                    931:          print_multi_reg (f, "ldmfd\tsp!",
                    932:                           (live_regs_mask & ~0x4000) | 0x8000, TRUE);
                    933:          code_size += 4;
                    934:        }
                    935:       else
                    936:        {
                    937:          if (live_regs_mask)
                    938:            {
                    939:              print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE);
                    940:              code_size += 4;
                    941:            }
                    942:          if (current_function_pretend_args_size)
                    943:            {
                    944:              operands[0] = operands[1] = stack_pointer_rtx;
                    945:              operands[2] = gen_rtx (CONST_INT, VOIDmode,
                    946:                                     current_function_pretend_args_size);
                    947:              output_add_immediate (operands);
                    948:            }
                    949:          fputs ("\tmovs\tpc, lr\n", f);
                    950:          code_size += 4;
                    951:        }
                    952:     }
                    953:   arm_increase_location (code_size);
                    954:   current_function_anonymous_args = 0;
                    955: } /* output_epilogue */
                    956: 
                    957: /* Increase the `arm_text_location' by AMOUNT if we're in the text
                    958:    segment.  */
                    959: 
                    960: void
                    961: arm_increase_location (amount)
                    962:      int amount;
                    963: {
                    964:   if (in_text_section ())
                    965:     arm_text_location += amount;
                    966: } /* arm_increase_location */
                    967: 
                    968: 
                    969: /* Like output_asm_insn (), but also increases the arm_text_location (if in
                    970:    the .text segment, of course, even though this will always be true).
                    971:    Returns the empty string.  */
                    972: 
                    973: char *
                    974: arm_output_asm_insn (template, operands)
                    975:      char *template;
                    976:      rtx *operands;
                    977: {
                    978:   extern FILE *asm_out_file;
                    979: 
                    980:   output_asm_insn (template, operands);
                    981:   if (in_text_section ())
                    982:     arm_text_location += 4;
                    983:   fflush (asm_out_file);
                    984:   return ("");
                    985: } /* arm_output_asm_insn */
                    986: 
                    987: 
                    988: /* Output a label definition.  If this label is within the .text segment, it
                    989:    is stored in OFFSET_TABLE, to be used when building `llc' instructions.
                    990:    Maybe GCC remembers names not starting with a `*' for a long time, but this
                    991:    is a minority anyway, so we just make a copy.  Do not store the leading `*'
                    992:    if the name starts with one.  */
                    993: 
                    994: void
                    995: arm_asm_output_label (stream, name)
                    996:      FILE *stream;
                    997:      char *name;
                    998: {
                    999:   char *real_name, *s;
                   1000:   struct label_offset *cur;
                   1001:   int hash = 0;
                   1002: 
                   1003:   assemble_name (stream, name);
                   1004:   fputs (":\n", stream);
                   1005:   if (! in_text_section ())
                   1006:     return;
                   1007: 
                   1008:   if (name[0] == '*')
                   1009:     {
                   1010:       real_name = xmalloc (1 + strlen (&name[1]));
                   1011:       strcpy (real_name, &name[1]);
                   1012:     }
                   1013:   else
                   1014:     {
                   1015:       real_name = xmalloc (2 + strlen (name));
                   1016:       strcpy (real_name, "_");
                   1017:       strcat (real_name, name);
                   1018:     }
                   1019:   for (s = real_name; *s; s++)
                   1020:     hash += *s;
                   1021:   hash = hash % LABEL_HASH_SIZE;
                   1022:   cur = (struct label_offset *) xmalloc (sizeof (struct label_offset));
                   1023:   cur->name = real_name;
                   1024:   cur->offset = arm_text_location;
                   1025:   cur->cdr = offset_table[hash];
                   1026:   offset_table[hash] = cur;
                   1027: } /* arm_asm_output_label */
                   1028: 
                   1029: 
                   1030: /* Output the instructions needed to perform what Martin's /bin/as called
                   1031:    llc: load an SImode thing from the function's constant pool.
                   1032: 
                   1033:    XXX This could be enhanced in that we do not really need a pointer in the
                   1034:    constant pool pointing to the real thing.  If we can address this pointer,
                   1035:    we can also address what it is pointing at, in fact, anything in the text
                   1036:    segment which has been defined already within this .s file.  */
                   1037: 
                   1038: char *
                   1039: arm_output_llc (operands)
                   1040:      rtx *operands;
                   1041: {
                   1042:   char *s, *name = XSTR (XEXP (operands[1], 0), 0);
                   1043:   struct label_offset *he;
                   1044:   int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4);
                   1045: 
                   1046:   if (*name != '*')
                   1047:     abort ();
                   1048: 
                   1049:   for (s = &name[1]; *s; s++)
                   1050:     hash += *s;
                   1051:   hash = hash % LABEL_HASH_SIZE;
                   1052:   he = offset_table[hash];
                   1053:   while (he && strcmp (he->name, &name[1]))
                   1054:     he = he->cdr;
                   1055: 
                   1056:   if (!he)
                   1057:     abort ();
                   1058: 
                   1059:   if (arm_text_location + 8 - he->offset < 4095)
                   1060:     {
                   1061:       fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n",
                   1062:               conditional ? arm_condition_codes[arm_current_cc] : "",
                   1063:               reg_names[REGNO (operands[0])], &name[1]);
                   1064:       arm_increase_location (4);
                   1065:       return ("");
                   1066:     }
                   1067:   else
                   1068:     {
                   1069:       int offset = - (arm_text_location + 8 - he->offset);
                   1070:       char *reg_name = reg_names[REGNO (operands[0])];
                   1071: 
                   1072:       /* ??? This is a hack, assuming the constant pool never is more than
                   1073:         (1 + 255) * 4096 == 1Meg away from the PC.  */
                   1074: 
                   1075:       if (offset > 1000000)
                   1076:        abort ();
                   1077: 
                   1078:       fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n",
                   1079:               conditional ? arm_condition_codes[arm_current_cc] : "",
                   1080:               reg_name, &name[1]);
                   1081:       fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n",
                   1082:               conditional ? arm_condition_codes[arm_current_cc] : "",
                   1083:               reg_name, reg_name, &name[1]);
                   1084:       arm_increase_location (8);
                   1085:     }
                   1086:   return ("");
                   1087: } /* arm_output_llc */
                   1088: 
                   1089: 
                   1090: /* Output code resembling an .lcomm directive.  /bin/as doesn't have this
                   1091:    directive hence this hack, which works by reserving some `.space' in the
                   1092:    bss segment directly.
                   1093: 
                   1094:    XXX This is a severe hack, which is guaranteed NOT to work since it doesn't
                   1095:    define STATIC COMMON space but merely STATIC BSS space.  */
                   1096: 
                   1097: void
                   1098: output_lcomm_directive (stream, name, size, rounded)
                   1099:      FILE *stream;
                   1100:      char *name;
                   1101:      int size, rounded;
                   1102: {
                   1103:   fputs ("\n\t.bss\t@ .lcomm\n", stream);
                   1104:   assemble_name (stream, name);
                   1105:   fprintf (stream, ":\t.space\t%d\n", rounded);
                   1106:   if (in_text_section ())
                   1107:     fputs ("\n\t.text\n", stream);
                   1108:   else
                   1109:     fputs ("\n\t.data\n", stream);
                   1110: } /* output_lcomm_directive */
                   1111: 
                   1112: /* A finite state machine takes care of noticing whether or not instructions
                   1113:    can be conditionally executed, and thus decrease execution time and code
                   1114:    size by deleting branch instructions.  The fsm is controlled by
                   1115:    final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE.  */
                   1116: 
                   1117: /* The state of the fsm controlling condition codes are:
                   1118:    0: normal, do nothing special
                   1119:    1: make ASM_OUTPUT_OPCODE not output this instruction
                   1120:    2: make ASM_OUTPUT_OPCODE not output this instruction
                   1121:    3: make instructions conditional
                   1122:    4: make instructions conditional
                   1123: 
                   1124:    State transitions (state->state by whom under condition):
                   1125:    0 -> 1 final_prescan_insn if the `target' is a label
                   1126:    0 -> 2 final_prescan_insn if the `target' is an unconditional branch
                   1127:    1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch
                   1128:    2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch
                   1129:    3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached
                   1130:           (the target label has CODE_LABEL_NUMBER equal to arm_target_label).
                   1131:    4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
                   1132:           (the target insn is arm_target_insn).
                   1133: 
                   1134:    XXX In case the `target' is an unconditional branch, this conditionalising
                   1135:    of the instructions always reduces code size, but not always execution
                   1136:    time.  But then, I want to reduce the code size to somewhere near what
                   1137:    /bin/cc produces.  */
                   1138: 
                   1139: /* The condition codes of the ARM, and the inverse function.  */
                   1140: char *arm_condition_codes[] =
                   1141: {
                   1142:   "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
                   1143:   "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
                   1144: };
                   1145: 
                   1146: #define ARM_INVERSE_CONDITION_CODE(X)  ((X) ^ 1)
                   1147: 
                   1148: /* Returns the index of the ARM condition code string in
                   1149:    `arm_condition_codes'.  COMPARISON should be an rtx like
                   1150:    `(eq (...) (...))'.  */
                   1151: 
                   1152: int
                   1153: get_arm_condition_code (comparison)
                   1154:      rtx comparison;
                   1155: {
                   1156:   switch (GET_CODE (comparison))
                   1157:     {
                   1158:     case NE: return (1);
                   1159:     case EQ: return (0);
                   1160:     case GE: return (10);
                   1161:     case GT: return (12);
                   1162:     case LE: return (13);
                   1163:     case LT: return (11);
                   1164:     case GEU: return (2);
                   1165:     case GTU: return (8);
                   1166:     case LEU: return (9);
                   1167:     case LTU: return (3);
                   1168:     default: abort ();
                   1169:     }
                   1170:   /*NOTREACHED*/
                   1171:   return (42);
                   1172: } /* get_arm_condition_code */
                   1173: 
                   1174: 
                   1175: void
                   1176: final_prescan_insn (insn, opvec, noperands)
                   1177:      rtx insn;
                   1178:      rtx *opvec;
                   1179:      int noperands;
                   1180: {
                   1181:   /* BODY will hold the body of INSN.  */
                   1182:   register rtx body = PATTERN (insn);
                   1183: 
                   1184:   /* This will be 1 if trying to repeat the trick, and things need to be
                   1185:      reversed if it appears to fail.  */
                   1186:   int reverse = 0;
                   1187: 
                   1188:   /* START_INSN will hold the insn from where we start looking.  This is the
                   1189:      first insn after the following code_label if REVERSE is true.  */
                   1190:   rtx start_insn = insn;
                   1191: 
                   1192:   /* If in state 4, check if the target branch is reached, in order to
                   1193:      change back to state 0.  */
                   1194:   if (arm_ccfsm_state == 4)
                   1195:     {
                   1196:       if (insn == arm_target_insn)
                   1197:        arm_ccfsm_state = 0;
                   1198:       return;
                   1199:     }
                   1200: 
                   1201:   /* If in state 3, it is possible to repeat the trick, if this insn is an
                   1202:      unconditional branch to a label, and immediately following this branch
                   1203:      is the previous target label which is only used once, and the label this
                   1204:      branch jumps to is not too far off.  */
                   1205:   if (arm_ccfsm_state == 3)
                   1206:     {
                   1207:       if (simplejump_p (insn))
                   1208:        {
                   1209:          start_insn = next_nonnote_insn (start_insn);
                   1210:          if (GET_CODE (start_insn) == BARRIER)
                   1211:            {
                   1212:              /* XXX Isn't this always a barrier?  */
                   1213:              start_insn = next_nonnote_insn (start_insn);
                   1214:            }
                   1215:          if (GET_CODE (start_insn) == CODE_LABEL
                   1216:              && CODE_LABEL_NUMBER (start_insn) == arm_target_label
                   1217:              && LABEL_NUSES (start_insn) == 1)
                   1218:            reverse = TRUE;
                   1219:          else
                   1220:            return;
                   1221:        }
                   1222:       else
                   1223:        return;
                   1224:     }
                   1225: 
                   1226:   if (arm_ccfsm_state != 0 && !reverse)
                   1227:     abort ();
                   1228:   if (GET_CODE (insn) != JUMP_INSN)
                   1229:     return;
                   1230: 
                   1231:   if (reverse
                   1232:       || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
                   1233:          && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
                   1234:     {
                   1235:       int insns_skipped = 0, fail = FALSE, succeed = FALSE;
                   1236:       /* Flag which part of the IF_THEN_ELSE is the LABEL_REF.  */
                   1237:       int then_not_else = TRUE;
                   1238:       rtx this_insn = start_insn, label;
                   1239: 
                   1240:       /* Register the insn jumped to.  */
                   1241:       if (reverse)
                   1242:        label = XEXP (SET_SRC (body), 0);
                   1243:       else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
                   1244:        label = XEXP (XEXP (SET_SRC (body), 1), 0);
                   1245:       else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
                   1246:        {
                   1247:          label = XEXP (XEXP (SET_SRC (body), 2), 0);
                   1248:          then_not_else = FALSE;
                   1249:        }
                   1250:       else
                   1251:        abort ();
                   1252: 
                   1253:       /* See how many insns this branch skips, and what kind of insns.  If all
                   1254:         insns are okay, and the label or unconditional branch to the same
                   1255:         label is not too far away, succeed.  */
                   1256:       for (insns_skipped = 0;
                   1257:           !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
                   1258:           insns_skipped++)
                   1259:        {
                   1260:          rtx scanbody;
                   1261: 
                   1262:          this_insn = next_nonnote_insn (this_insn);
                   1263:          if (!this_insn)
                   1264:            break;
                   1265: 
                   1266:          scanbody = PATTERN (this_insn);
                   1267: 
                   1268:          switch (GET_CODE (this_insn))
                   1269:            {
                   1270:            case CODE_LABEL:
                   1271:              /* Succeed if it is the target label, otherwise fail since
                   1272:                 control falls in from somewhere else.  */
                   1273:              if (this_insn == label)
                   1274:                {
                   1275:                  arm_ccfsm_state = 1;
                   1276:                  succeed = TRUE;
                   1277:                }
                   1278:              else
                   1279:                fail = TRUE;
                   1280:              break;
                   1281: 
                   1282:            case BARRIER:       /* XXX Is this case necessary?  */
                   1283:              /* Succeed if the following insn is the target label.
                   1284:                 Otherwise fail.  */
                   1285:              this_insn = next_nonnote_insn (this_insn);
                   1286:              if (this_insn == label)
                   1287:                {
                   1288:                  arm_ccfsm_state = 1;
                   1289:                  succeed = TRUE;
                   1290:                }
                   1291:              else
                   1292:                fail = TRUE;
                   1293:              break;
                   1294: 
                   1295:            case JUMP_INSN:
                   1296:              /* If this is an unconditional branch to the same label, succeed.
                   1297:                 If it is to another label, do nothing.  If it is conditional,
                   1298:                 fail.  */
                   1299:              /* XXX Probably, the test for the SET and the PC are unnecessary. */
                   1300: 
                   1301:              if (GET_CODE (scanbody) == SET && GET_CODE (SET_DEST (scanbody)) == PC)
                   1302:                {
                   1303:                  if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
                   1304:                      && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
                   1305:                    {
                   1306:                      arm_ccfsm_state = 2;
                   1307:                      succeed = TRUE;
                   1308:                    }
                   1309:                  else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
                   1310:                    fail = TRUE;
                   1311:                }
                   1312:              break;
                   1313: 
                   1314:            case INSN:
                   1315:              /* Instructions affecting the condition codes make it fail.  */
                   1316:              if (sets_cc0_p (scanbody))
                   1317:                fail = TRUE;
                   1318:              break;
                   1319: 
                   1320:            default:
                   1321:              break;
                   1322:            }
                   1323:        }
                   1324:       if (succeed)
                   1325:        {
                   1326:          if (arm_ccfsm_state == 1 || reverse)
                   1327:            arm_target_label = CODE_LABEL_NUMBER (label);
                   1328:          else if (arm_ccfsm_state == 2)
                   1329:            arm_target_insn = this_insn;
                   1330:          else
                   1331:            abort ();
                   1332: 
                   1333:          /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what
                   1334:             it was.  */
                   1335:          if (!reverse)
                   1336:            arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0));
                   1337:          if (reverse || then_not_else)
                   1338:            arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
                   1339:        }
                   1340:     }
                   1341: } /* final_prescan_insn */
                   1342: 
                   1343: /* EOF */

unix.superglobalmegacorp.com

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