|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.