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