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