|
|
1.1 root 1: /* Output routines for GCC for ARM/RISCiX. 1.1.1.2 ! root 2: Copyright (C) 1991, 1993 Free Software Foundation, Inc. 1.1 root 3: Contributed by Pieter `Tiggr' Schoenmakers ([email protected]) 4: and Martin Simmons (@harleqn.co.uk). 1.1.1.2 ! root 5: More major hacks by Richard Earnshaw ([email protected]) 1.1 root 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. */ 1.1.1.2 ! root 22: 1.1 root 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" 1.1.1.2 ! root 36: #include "reload.h" 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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) 1.1.1.2 ! root 140: HOST_WIDE_INT i; 1.1 root 141: { 1.1.1.2 ! root 142: unsigned HOST_WIDE_INT mask = ~0xFF; 1.1 root 143: 144: do 145: { 1.1.1.2 ! root 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); 1.1 root 151: } while (mask != ~0xFF); 152: 153: return (FALSE); 154: } /* const_ok_for_arm */ 155: 1.1.1.2 ! root 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: 1.1 root 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: { 1.1.1.2 ! root 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; 1.1 root 206: } /* const_double_rtx_ok_for_fpu */ 1.1.1.2 ! root 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 */ 1.1 root 229: 230: /* Predicates for `match_operand' and `match_operator'. */ 231: 1.1.1.2 ! root 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: 1.1 root 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: { 1.1.1.2 ! root 277: return (s_register_operand (op, mode) 1.1 root 278: || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); 279: } /* arm_rhs_operand */ 280: 1.1.1.2 ! root 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: 1.1 root 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: { 1.1.1.2 ! root 326: if (s_register_operand (op, mode)) 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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. 1.1.1.2 ! root 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)). */ 1.1 root 365: 366: int 367: di_operand (op, mode) 368: rtx op; 369: enum machine_mode mode; 370: { 1.1.1.2 ! root 371: if (s_register_operand (op, mode)) 1.1 root 372: return (TRUE); 373: 374: switch (GET_CODE (op)) 375: { 376: case CONST_DOUBLE: 377: case CONST_INT: 378: return (TRUE); 379: case MEM: 1.1.1.2 ! root 380: return (memory_address_p (DImode, XEXP (op, 0))); 1.1 root 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: { 1.1.1.2 ! root 393: return (s_register_operand(op, mode) ! 394: || (immediate_operand (op, mode) ! 395: && INTVAL (op) < 4096 && INTVAL (op) > -4096)); 1.1 root 396: } /* index_operand */ 397: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 444: if (code == MULT) ! 445: return power_of_two_operand (XEXP (x, 1)); 1.1 root 446: return (code == ASHIFT || code == LSHIFT 447: || code == ASHIFTRT || code == LSHIFTRT); 448: } 449: } /* shift_operator */ 1.1.1.2 ! root 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: 1.1 root 814: 815: /* Routines to output assembly language. */ 816: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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])); 1.1.1.2 ! root 1093: output_mov_immediate (operands, FALSE, ""); ! 1094: output_mov_immediate (otherops, FALSE, ""); 1.1 root 1095: } 1096: else if (code1 == CONST_INT) 1097: { 1098: otherops[1] = const0_rtx; 1.1.1.2 ! root 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, ""); 1.1 root 1107: } 1108: else if (code1 == MEM) 1109: { 1.1.1.2 ! root 1110: switch (GET_CODE (XEXP (operands[1], 0))) 1.1 root 1111: { 1.1.1.2 ! root 1112: case REG: 1.1 root 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); 1.1.1.2 ! root 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: 1.1 root 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(); 1.1.1.2 ! root 1158: switch (GET_CODE (XEXP (operands[0], 0))) ! 1159: { ! 1160: case REG: 1.1 root 1161: operands[0] = XEXP (operands[0], 0); 1162: arm_output_asm_insn ("stmia\t%0, %M1", operands); 1.1.1.2 ! root 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: 1.1 root 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; 1.1.1.2 ! root 1358: case MULT: ! 1359: *shift_ptr = gen_rtx (CONST_INT, VOIDmode, ! 1360: int_log2 (INTVAL (*shift_ptr))); ! 1361: return ("asl"); 1.1 root 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: 1.1.1.2 ! root 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 */ 1.1 root 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; 1.1.1.2 ! root 1510: unsigned char *p; 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 1760: fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n", 1.1.1.2 ! root 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", 1.1 root 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: 1.1.1.2 ! root 1773: if (frame_pointer_needed) 1.1 root 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]) 1.1.1.2 ! root 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: } 1.1 root 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); 1.1.1.2 ! root 1805: code_size += 4; 1.1 root 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: { 1.1.1.2 ! root 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; 1.1 root 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: 1.1.1.2 ! root 1836: if (frame_pointer_needed) 1.1 root 1837: { 1838: /* Make `fp' point to saved value of `pc'. */ 1839: 1.1.1.2 ! root 1840: operands[0] = gen_rtx (REG, SImode, HARD_FRAME_POINTER_REGNUM); 1.1 root 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: { 1.1.1.2 ! root 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; 1.1 root 1866: rtx operands[3]; 1867: 1.1.1.2 ! root 1868: if (use_return_insn() && return_used_this_function) 1.1 root 1869: { 1.1.1.2 ! root 1870: if (frame_size && !(frame_pointer_needed || TARGET_APCS)) ! 1871: { ! 1872: abort (); ! 1873: } ! 1874: return; 1.1 root 1875: } 1876: 1.1.1.2 ! root 1877: for (reg = 4; reg <= 10; reg++) 1.1 root 1878: if (regs_ever_live[reg]) 1879: { 1.1.1.2 ! root 1880: live_regs_mask |= (1 << reg); ! 1881: floats_offset += 4; 1.1 root 1882: } 1883: 1.1.1.2 ! root 1884: ! 1885: if (frame_pointer_needed) 1.1 root 1886: { 1.1.1.2 ! root 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); 1.1 root 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: 1.1.1.2 ! root 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: } 1.1 root 1917: if (current_function_pretend_args_size == 0 && regs_ever_live[14]) 1918: { 1.1.1.2 ! root 1919: print_multi_reg (f, "ldmfd\tsp!", live_regs_mask | 0x8000, ! 1920: TARGET_6 ? FALSE : TRUE); 1.1 root 1921: code_size += 4; 1922: } 1923: else 1924: { 1.1.1.2 ! root 1925: if (live_regs_mask || regs_ever_live[14]) 1.1 root 1926: { 1.1.1.2 ! root 1927: live_regs_mask |= 0x4000; 1.1 root 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: } 1.1.1.2 ! root 1938: fputs (TARGET_6 ? "\tmov\tpc, lr\n" : "\tmovs\tpc, lr\n", f); 1.1 root 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: 1.1.1.2 ! root 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: } 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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) 1.1.1.2 ! root 2265: { ! 2266: arm_target_insn = NULL; 1.1 root 2267: arm_ccfsm_state = 0; 1.1.1.2 ! root 2268: } 1.1 root 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: } 1.1.1.2 ! root 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: } 1.1 root 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: 1.1.1.2 ! root 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: 1.1 root 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; 1.1.1.2 ! root 2338: rtx this_insn = start_insn, label = 0; 1.1 root 2339: 1.1.1.2 ! root 2340: if (get_attr_conds (insn) == CONDS_JUMP_CLOB) ! 2341: jump_clobbers = 1; ! 2342: 1.1 root 2343: /* Register the insn jumped to. */ 2344: if (reverse) 1.1.1.2 ! root 2345: { ! 2346: if (!seeking_return) ! 2347: label = XEXP (SET_SRC (body), 0); ! 2348: } 1.1 root 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: } 1.1.1.2 ! root 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: } 1.1 root 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: { 1.1.1.2 ! root 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; 1.1 root 2395: succeed = TRUE; 2396: } 2397: else 2398: fail = TRUE; 2399: break; 2400: 1.1.1.2 ! root 2401: case BARRIER: 1.1 root 2402: /* Succeed if the following insn is the target label. 1.1.1.2 ! root 2403: Otherwise fail. ! 2404: If return insns are used then the last insn in a function ! 2405: will be a barrier. */ 1.1 root 2406: this_insn = next_nonnote_insn (this_insn); 1.1.1.2 ! root 2407: if (this_insn && this_insn == label) 1.1 root 2408: { 1.1.1.2 ! root 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; 1.1 root 2416: succeed = TRUE; 2417: } 2418: else 2419: fail = TRUE; 2420: break; 2421: 1.1.1.2 ! root 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; 1.1 root 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: 1.1.1.2 ! root 2434: if (GET_CODE (scanbody) == SET ! 2435: && GET_CODE (SET_DEST (scanbody)) == PC) 1.1 root 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: } 1.1.1.2 ! root 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: } 1.1 root 2463: break; 2464: 2465: case INSN: 1.1.1.2 ! root 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) 1.1 root 2471: fail = TRUE; 2472: break; 2473: 2474: default: 2475: break; 2476: } 2477: } 2478: if (succeed) 2479: { 1.1.1.2 ! root 2480: if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse)) 1.1 root 2481: arm_target_label = CODE_LABEL_NUMBER (label); 1.1.1.2 ! root 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: } 1.1 root 2501: else 2502: abort (); 1.1.1.2 ! root 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: } 1.1 root 2523: 2524: if (reverse || then_not_else) 2525: arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); 2526: } 1.1.1.2 ! root 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); 1.1 root 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.