|
|
1.1 ! root 1: /* Subroutines for insn-output.c for Intel 860 ! 2: Copyright (C) 1989 Free Software Foundation, Inc. ! 3: Derived from out-sparc.c. ! 4: ! 5: This file is part of GNU CC. ! 6: ! 7: GNU CC is free software; you can redistribute it and/or modify ! 8: it under the terms of the GNU General Public License as published by ! 9: the Free Software Foundation; either version 1, or (at your option) ! 10: any later version. ! 11: ! 12: GNU CC is distributed in the hope that it will be useful, ! 13: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 15: GNU General Public License for more details. ! 16: ! 17: You should have received a copy of the GNU General Public License ! 18: along with GNU CC; see the file COPYING. If not, write to ! 19: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 20: ! 21: ! 22: /* Global variables for machine-dependend things. */ ! 23: ! 24: /* This should go away if we pass floats to regs via ! 25: the stack instead of the frame, and if we learn how ! 26: to renumber all the registers when we don't do a save (hard!). */ ! 27: extern int frame_pointer_needed; ! 28: ! 29: static rtx find_addr_reg (); ! 30: ! 31: /* Return non-zero only if OP is a register of mode MODE, ! 32: or const0_rtx. */ ! 33: int ! 34: reg_or_0_operand (op, mode) ! 35: rtx op; ! 36: enum machine_mode mode; ! 37: { ! 38: return (op == const0_rtx || register_operand (op, mode) ! 39: || op == CONST0_RTX (mode)); ! 40: } ! 41: ! 42: /* Return non-zero if this pattern, can be evaluated safely, even if it ! 43: was not asked for. */ ! 44: int ! 45: safe_insn_src_p (op, mode) ! 46: rtx op; ! 47: enum machine_mode mode; ! 48: { ! 49: /* Just experimenting. */ ! 50: ! 51: /* No floating point src is safe if it contains an arithmetic ! 52: operation, since that operation may trap. */ ! 53: switch (GET_CODE (op)) ! 54: { ! 55: case CONST_INT: ! 56: case LABEL_REF: ! 57: case SYMBOL_REF: ! 58: case CONST: ! 59: return 1; ! 60: ! 61: case REG: ! 62: return 1; ! 63: ! 64: case MEM: ! 65: return CONSTANT_ADDRESS_P (XEXP (op, 0)); ! 66: ! 67: /* We never need to negate or complement constants. */ ! 68: case NEG: ! 69: return (mode != SFmode && mode != DFmode); ! 70: case NOT: ! 71: case ZERO_EXTEND: ! 72: return 1; ! 73: ! 74: case EQ: ! 75: case NE: ! 76: case LT: ! 77: case GT: ! 78: case LE: ! 79: case GE: ! 80: case LTU: ! 81: case GTU: ! 82: case LEU: ! 83: case GEU: ! 84: case MINUS: ! 85: case PLUS: ! 86: return (mode != SFmode && mode != DFmode); ! 87: case AND: ! 88: case IOR: ! 89: case XOR: ! 90: case LSHIFT: ! 91: case ASHIFT: ! 92: case ASHIFTRT: ! 93: case LSHIFTRT: ! 94: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) ! 95: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) ! 96: return 0; ! 97: return 1; ! 98: ! 99: default: ! 100: return 0; ! 101: } ! 102: } ! 103: ! 104: /* Return 1 if REG is clobbered in IN. ! 105: Return 2 if REG is used in IN. ! 106: Return 3 if REG is both used and clobbered in IN. ! 107: Return 0 if neither. */ ! 108: ! 109: static int ! 110: reg_clobbered_p (reg, in) ! 111: rtx reg; ! 112: rtx in; ! 113: { ! 114: register enum rtx_code code; ! 115: ! 116: if (in == 0) ! 117: return 0; ! 118: ! 119: code = GET_CODE (in); ! 120: ! 121: if (code == SET || code == CLOBBER) ! 122: { ! 123: rtx dest = SET_DEST (in); ! 124: int set = 0; ! 125: int used = 0; ! 126: ! 127: while (GET_CODE (dest) == STRICT_LOW_PART ! 128: || GET_CODE (dest) == SUBREG ! 129: || GET_CODE (dest) == SIGN_EXTRACT ! 130: || GET_CODE (dest) == ZERO_EXTRACT) ! 131: dest = XEXP (dest, 0); ! 132: ! 133: if (dest == reg) ! 134: set = 1; ! 135: else if (GET_CODE (dest) == REG ! 136: && refers_to_regno_p (REGNO (reg), ! 137: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 138: SET_DEST (in), 0)) ! 139: { ! 140: set = 1; ! 141: /* Anything that sets just part of the register ! 142: is considered using as well as setting it. ! 143: But note that a straight SUBREG of a single-word value ! 144: clobbers the entire value. */ ! 145: if (dest != SET_DEST (in) ! 146: && ! (GET_CODE (SET_DEST (in)) == SUBREG ! 147: || UNITS_PER_WORD >= GET_MODE_SIZE (GET_MODE (dest)))) ! 148: used = 1; ! 149: } ! 150: ! 151: if (code == SET) ! 152: { ! 153: if (set) ! 154: used = refers_to_regno_p (REGNO (reg), ! 155: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 156: SET_SRC (in), 0); ! 157: else ! 158: used = refers_to_regno_p (REGNO (reg), ! 159: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 160: in, 0); ! 161: } ! 162: ! 163: return set + used * 2; ! 164: } ! 165: ! 166: if (refers_to_regno_p (REGNO (reg), ! 167: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 168: in, 0)) ! 169: return 2; ! 170: return 0; ! 171: } ! 172: ! 173: /* Return non-zero if OP can be written to without screwing up ! 174: GCC's model of what's going on. It is assumed that this operand ! 175: appears in the dest position of a SET insn in a conditional ! 176: branch's delay slot. AFTER is the label to start looking from. */ ! 177: int ! 178: operand_clobbered_before_used_after (op, after) ! 179: rtx op; ! 180: rtx after; ! 181: { ! 182: extern char call_used_regs[]; ! 183: ! 184: /* Just experimenting. */ ! 185: if (GET_CODE (op) == CC0) ! 186: return 1; ! 187: if (GET_CODE (op) == REG) ! 188: { ! 189: rtx insn; ! 190: ! 191: if (op == stack_pointer_rtx) ! 192: return 0; ! 193: ! 194: /* Scan forward from the label, to see if the value of OP ! 195: is clobbered before the first use. */ ! 196: ! 197: for (insn = NEXT_INSN (after); insn; insn = NEXT_INSN (insn)) ! 198: { ! 199: if (GET_CODE (insn) == NOTE) ! 200: continue; ! 201: if (GET_CODE (insn) == INSN ! 202: || GET_CODE (insn) == JUMP_INSN ! 203: || GET_CODE (insn) == CALL_INSN) ! 204: { ! 205: switch (reg_clobbered_p (op, PATTERN (insn))) ! 206: { ! 207: default: ! 208: return 0; ! 209: case 1: ! 210: return 1; ! 211: case 0: ! 212: break; ! 213: } ! 214: } ! 215: /* If we reach another label without clobbering OP, ! 216: then we cannot safely write it here. */ ! 217: else if (GET_CODE (insn) == CODE_LABEL) ! 218: return 0; ! 219: if (GET_CODE (insn) == JUMP_INSN) ! 220: { ! 221: if (condjump_p (insn)) ! 222: return 0; ! 223: /* This is a jump insn which has already ! 224: been mangled. We can't tell what it does. */ ! 225: if (GET_CODE (PATTERN (insn)) == PARALLEL) ! 226: return 0; ! 227: if (! JUMP_LABEL (insn)) ! 228: return 0; ! 229: /* Keep following jumps. */ ! 230: insn = JUMP_LABEL (insn); ! 231: } ! 232: } ! 233: return 1; ! 234: } ! 235: ! 236: /* In both of these cases, the first insn executed ! 237: for this op will be a orh whatever%h,r0,r31, ! 238: which is tolerable. */ ! 239: if (GET_CODE (op) == MEM) ! 240: return (CONSTANT_ADDRESS_P (XEXP (op, 0))); ! 241: ! 242: return 0; ! 243: } ! 244: ! 245: /* Return non-zero if this pattern, as a source to a "SET", ! 246: is known to yield an instruction of unit size. */ ! 247: int ! 248: single_insn_src_p (op, mode) ! 249: rtx op; ! 250: enum machine_mode mode; ! 251: { ! 252: switch (GET_CODE (op)) ! 253: { ! 254: case CONST_INT: ! 255: /* This is not always a single insn src, technically, ! 256: but output_delayed_branch knows how to deal with it. */ ! 257: return 1; ! 258: ! 259: case SYMBOL_REF: ! 260: case CONST: ! 261: /* This is not a single insn src, technically, ! 262: but output_delayed_branch knows how to deal with it. */ ! 263: return 1; ! 264: ! 265: case REG: ! 266: return 1; ! 267: ! 268: case MEM: ! 269: return 1; ! 270: ! 271: /* We never need to negate or complement constants. */ ! 272: case NEG: ! 273: return (mode != DFmode); ! 274: case NOT: ! 275: case ZERO_EXTEND: ! 276: return 1; ! 277: ! 278: case EQ: ! 279: case NE: ! 280: case LT: ! 281: case GT: ! 282: case LE: ! 283: case GE: ! 284: case LTU: ! 285: case GTU: ! 286: case LEU: ! 287: case GEU: ! 288: case MINUS: ! 289: case PLUS: ! 290: /* Not doing floating point, since they probably ! 291: take longer than the branch slot they might fill. */ ! 292: return (mode != SFmode && mode != DFmode); ! 293: case AND: ! 294: case IOR: ! 295: case XOR: ! 296: case LSHIFT: ! 297: case ASHIFT: ! 298: case ASHIFTRT: ! 299: case LSHIFTRT: ! 300: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) ! 301: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) ! 302: return 0; ! 303: return 1; ! 304: ! 305: case SUBREG: ! 306: if (SUBREG_WORD (op) != 0) ! 307: return 0; ! 308: return single_insn_src_p (SUBREG_REG (op), mode); ! 309: ! 310: /* Not doing floating point, since they probably ! 311: take longer than the branch slot they might fill. */ ! 312: case FLOAT_EXTEND: ! 313: case FLOAT_TRUNCATE: ! 314: case FLOAT: ! 315: case FIX: ! 316: case UNSIGNED_FLOAT: ! 317: case UNSIGNED_FIX: ! 318: return 0; ! 319: ! 320: default: ! 321: return 0; ! 322: } ! 323: } ! 324: ! 325: /* Nonzero only if this *really* is a single insn operand. */ ! 326: int ! 327: strict_single_insn_op_p (op, mode) ! 328: rtx op; ! 329: enum machine_mode mode; ! 330: { ! 331: if (mode == VOIDmode) ! 332: mode = GET_MODE (op); ! 333: ! 334: switch (GET_CODE (op)) ! 335: { ! 336: case CC0: ! 337: return 1; ! 338: ! 339: case CONST_INT: ! 340: if (SMALL_INT (op)) ! 341: return 1; ! 342: /* We can put this set insn into delay slot, because this is one ! 343: insn; 'sethi'. */ ! 344: if ((INTVAL (op) & 0x3ff) == 0) ! 345: return 1; ! 346: return 0; ! 347: ! 348: case SYMBOL_REF: ! 349: return 0; ! 350: ! 351: case REG: ! 352: #if 0 ! 353: /* This loses when moving an freg to a general reg. */ ! 354: return HARD_REGNO_NREGS (REGNO (op), mode) == 1; ! 355: #endif ! 356: return (mode != DFmode && mode != DImode); ! 357: ! 358: case MEM: ! 359: if (! CONSTANT_ADDRESS_P (XEXP (op, 0))) ! 360: return (mode != DFmode && mode != DImode); ! 361: return 0; ! 362: ! 363: /* We never need to negate or complement constants. */ ! 364: case NEG: ! 365: return (mode != DFmode); ! 366: case NOT: ! 367: case ZERO_EXTEND: ! 368: return 1; ! 369: ! 370: case EQ: ! 371: case NE: ! 372: case LT: ! 373: case GT: ! 374: case LE: ! 375: case GE: ! 376: case LTU: ! 377: case GTU: ! 378: case LEU: ! 379: case GEU: ! 380: case MINUS: ! 381: case PLUS: ! 382: case AND: ! 383: case IOR: ! 384: case XOR: ! 385: case LSHIFT: ! 386: case ASHIFT: ! 387: case ASHIFTRT: ! 388: case LSHIFTRT: ! 389: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) ! 390: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) ! 391: return 0; ! 392: return 1; ! 393: ! 394: case SUBREG: ! 395: if (SUBREG_WORD (op) != 0) ! 396: return 0; ! 397: return strict_single_insn_op_p (SUBREG_REG (op), mode); ! 398: ! 399: case SIGN_EXTEND: ! 400: if (GET_CODE (XEXP (op, 0)) == MEM ! 401: && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0))) ! 402: return 1; ! 403: return 0; ! 404: ! 405: /* Not doing floating point, since they probably ! 406: take longer than the branch slot they might fill. */ ! 407: case FLOAT_EXTEND: ! 408: case FLOAT_TRUNCATE: ! 409: case FLOAT: ! 410: case FIX: ! 411: case UNSIGNED_FLOAT: ! 412: case UNSIGNED_FIX: ! 413: return 0; ! 414: ! 415: default: ! 416: return 0; ! 417: } ! 418: } ! 419: ! 420: /* Return truth value of whether OP is a relational operator. */ ! 421: int ! 422: relop (op, mode) ! 423: rtx op; ! 424: enum machine_mode mode; ! 425: { ! 426: switch (GET_CODE (op)) ! 427: { ! 428: case EQ: ! 429: case NE: ! 430: case GT: ! 431: case GE: ! 432: case LT: ! 433: case LE: ! 434: case GTU: ! 435: case GEU: ! 436: case LTU: ! 437: case LEU: ! 438: return 1; ! 439: } ! 440: return 0; ! 441: } ! 442: ! 443: /* Return truth value of whether OP can be used as an operands in a three ! 444: address add/subtract insn (such as add %o1,7,%l2) of mode MODE. */ ! 445: ! 446: int ! 447: arith_operand (op, mode) ! 448: rtx op; ! 449: enum machine_mode mode; ! 450: { ! 451: return (register_operand (op, mode) ! 452: || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); ! 453: } ! 454: ! 455: /* Return 1 if OP is a valid first operand for a logical insn of mode MODE. */ ! 456: ! 457: int ! 458: logic_operand (op, mode) ! 459: rtx op; ! 460: enum machine_mode mode; ! 461: { ! 462: return (register_operand (op, mode) ! 463: || (GET_CODE (op) == CONST_INT && LOGIC_INT (op))); ! 464: } ! 465: ! 466: /* Return 1 if OP is a valid first operand for either a logical insn ! 467: or an add insn of mode MODE. */ ! 468: ! 469: int ! 470: compare_operand (op, mode) ! 471: rtx op; ! 472: enum machine_mode mode; ! 473: { ! 474: return (register_operand (op, mode) ! 475: || (GET_CODE (op) == CONST_INT && SMALL_INT (op) && LOGIC_INT (op))); ! 476: } ! 477: ! 478: /* Return truth value of whether OP can be used as an operand ! 479: of a bte insn. */ ! 480: ! 481: int ! 482: bte_operand (op, mode) ! 483: rtx op; ! 484: enum machine_mode mode; ! 485: { ! 486: return (register_operand (op, mode) ! 487: || (GET_CODE (op) == CONST_INT ! 488: && (unsigned) INTVAL (op) < 0x20)); ! 489: } ! 490: ! 491: /* Return 1 if OP is an indexed memory reference of mode MODE. */ ! 492: ! 493: int ! 494: indexed_operand (op, mode) ! 495: rtx op; ! 496: enum machine_mode mode; ! 497: { ! 498: return (GET_CODE (op) == MEM && GET_MODE (op) == mode ! 499: && GET_CODE (XEXP (op, 0)) == PLUS ! 500: && GET_MODE (XEXP (op, 0)) == SImode ! 501: && register_operand (XEXP (XEXP (op, 0), 0), SImode) ! 502: && register_operand (XEXP (XEXP (op, 0), 1), SImode)); ! 503: } ! 504: ! 505: /* Return 1 if OP is a suitable source operand for a load insn ! 506: with mode MODE. */ ! 507: ! 508: int ! 509: load_operand (op, mode) ! 510: rtx op; ! 511: enum machine_mode mode; ! 512: { ! 513: return (memory_operand (op, mode) || indexed_operand (op, mode)); ! 514: } ! 515: ! 516: /* Return truth value of whether OP is a integer which fits the ! 517: range constraining immediate operands in add/subtract insns. */ ! 518: ! 519: int ! 520: small_int (op, mode) ! 521: rtx op; ! 522: enum machine_mode mode; ! 523: { ! 524: return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); ! 525: } ! 526: ! 527: /* Return truth value of whether OP is a integer which fits the ! 528: range constraining immediate operands in logic insns. */ ! 529: ! 530: int ! 531: logic_int (op, mode) ! 532: rtx op; ! 533: enum machine_mode mode; ! 534: { ! 535: return (GET_CODE (op) == CONST_INT && LOGIC_INT (op)); ! 536: } ! 537: ! 538: /* Return the best assembler insn template ! 539: for moving operands[1] into operands[0] as a fullword. */ ! 540: ! 541: static char * ! 542: singlemove_string (operands) ! 543: rtx *operands; ! 544: { ! 545: if (GET_CODE (operands[0]) == MEM) ! 546: { ! 547: if (GET_CODE (operands[1]) != MEM) ! 548: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) ! 549: { ! 550: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 551: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 552: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 553: output_asm_insn ("orh ha%%%m0,r0,r31", operands); ! 554: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 555: cc_status.mdep = XEXP (operands[0], 0); ! 556: return "st.l %r1,l%%%m0(r31)"; ! 557: } ! 558: else ! 559: return "st.l %r1,%0"; ! 560: else ! 561: abort (); ! 562: #if 0 ! 563: { ! 564: rtx xoperands[2]; ! 565: ! 566: cc_status.flags &= ~CC_F0_IS_0; ! 567: xoperands[0] = gen_rtx (REG, SFmode, 32); ! 568: xoperands[1] = operands[1]; ! 569: output_asm_insn (singlemove_string (xoperands), xoperands); ! 570: xoperands[1] = xoperands[0]; ! 571: xoperands[0] = operands[0]; ! 572: output_asm_insn (singlemove_string (xoperands), xoperands); ! 573: return ""; ! 574: } ! 575: #endif ! 576: } ! 577: if (GET_CODE (operands[1]) == MEM) ! 578: { ! 579: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) ! 580: { ! 581: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 582: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 583: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 584: output_asm_insn ("orh ha%%%m1,r0,r31", operands); ! 585: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 586: cc_status.mdep = XEXP (operands[1], 0); ! 587: return "ld.l l%%%m1(r31),%0"; ! 588: } ! 589: return "ld.l %1,%0"; ! 590: } ! 591: return "mov %1,%0"; ! 592: } ! 593: ! 594: /* Output assembler code to perform a doubleword move insn ! 595: with operands OPERANDS. */ ! 596: ! 597: char * ! 598: output_move_double (operands) ! 599: rtx *operands; ! 600: { ! 601: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 602: rtx latehalf[2]; ! 603: rtx addreg0 = 0, addreg1 = 0; ! 604: ! 605: /* First classify both operands. */ ! 606: ! 607: if (REG_P (operands[0])) ! 608: optype0 = REGOP; ! 609: else if (offsettable_memref_p (operands[0])) ! 610: optype0 = OFFSOP; ! 611: else if (GET_CODE (operands[0]) == MEM) ! 612: optype0 = MEMOP; ! 613: else ! 614: optype0 = RNDOP; ! 615: ! 616: if (REG_P (operands[1])) ! 617: optype1 = REGOP; ! 618: else if (CONSTANT_P (operands[1]) ! 619: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 620: optype1 = CNSTOP; ! 621: else if (offsettable_memref_p (operands[1])) ! 622: optype1 = OFFSOP; ! 623: else if (GET_CODE (operands[1]) == MEM) ! 624: optype1 = MEMOP; ! 625: else ! 626: optype1 = RNDOP; ! 627: ! 628: /* Check for the cases that the operand constraints are not ! 629: supposed to allow to happen. Abort if we get one, ! 630: because generating code for these cases is painful. */ ! 631: ! 632: if (optype0 == RNDOP || optype1 == RNDOP) ! 633: abort (); ! 634: ! 635: /* If an operand is an unoffsettable memory ref, find a register ! 636: we can increment temporarily to make it refer to the second word. */ ! 637: ! 638: if (optype0 == MEMOP) ! 639: addreg0 = find_addr_reg (XEXP (operands[0], 0)); ! 640: ! 641: if (optype1 == MEMOP) ! 642: addreg1 = find_addr_reg (XEXP (operands[1], 0)); ! 643: ! 644: /* ??? Perhaps in some cases move double words ! 645: if there is a spare pair of floating regs. */ ! 646: ! 647: /* Ok, we can do one word at a time. ! 648: Normally we do the low-numbered word first, ! 649: but if either operand is autodecrementing then we ! 650: do the high-numbered word first. ! 651: ! 652: In either case, set up in LATEHALF the operands to use ! 653: for the high-numbered word and in some cases alter the ! 654: operands in OPERANDS to be suitable for the low-numbered word. */ ! 655: ! 656: if (optype0 == REGOP) ! 657: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 658: else if (optype0 == OFFSOP) ! 659: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 660: else ! 661: latehalf[0] = operands[0]; ! 662: ! 663: if (optype1 == REGOP) ! 664: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 665: else if (optype1 == OFFSOP) ! 666: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 667: else if (optype1 == CNSTOP) ! 668: { ! 669: if (CONSTANT_P (operands[1])) ! 670: latehalf[1] = const0_rtx; ! 671: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 672: { ! 673: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 674: CONST_DOUBLE_HIGH (operands[1])); ! 675: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 676: CONST_DOUBLE_LOW (operands[1])); ! 677: } ! 678: } ! 679: else ! 680: latehalf[1] = operands[1]; ! 681: ! 682: /* If the first move would clobber the source of the second one, ! 683: do them in the other order. ! 684: ! 685: RMS says "This happens only for registers; ! 686: such overlap can't happen in memory unless the user explicitly ! 687: sets it up, and that is an undefined circumstance." ! 688: ! 689: but it happens on the sparc when loading parameter registers, ! 690: so I am going to define that circumstance, and make it work ! 691: as expected. */ ! 692: ! 693: if (optype0 == REGOP && optype1 == REGOP ! 694: && REGNO (operands[0]) == REGNO (latehalf[1])) ! 695: { ! 696: /* Make any unoffsettable addresses point at high-numbered word. */ ! 697: if (addreg0) ! 698: output_asm_insn ("adds 0x4,%0,%0", &addreg0); ! 699: if (addreg1) ! 700: output_asm_insn ("adds 0x4,%0,%0", &addreg1); ! 701: ! 702: /* Do that word. */ ! 703: output_asm_insn (singlemove_string (latehalf), latehalf); ! 704: ! 705: /* Undo the adds we just did. */ ! 706: if (addreg0) ! 707: output_asm_insn ("adds -0x4,%0,%0", &addreg0); ! 708: if (addreg1) ! 709: output_asm_insn ("adds -0x4,%0,%0", &addreg1); ! 710: ! 711: /* Do low-numbered word. */ ! 712: return singlemove_string (operands); ! 713: } ! 714: else if (optype0 == REGOP && optype1 != REGOP ! 715: && reg_overlap_mentioned_p (operands[0], operands[1])) ! 716: { ! 717: /* Do the late half first. */ ! 718: output_asm_insn (singlemove_string (latehalf), latehalf); ! 719: /* Then clobber. */ ! 720: return singlemove_string (operands); ! 721: } ! 722: ! 723: /* Normal case: do the two words, low-numbered first. */ ! 724: ! 725: output_asm_insn (singlemove_string (operands), operands); ! 726: ! 727: /* Make any unoffsettable addresses point at high-numbered word. */ ! 728: if (addreg0) ! 729: output_asm_insn ("adds 0x4,%0,%0", &addreg0); ! 730: if (addreg1) ! 731: output_asm_insn ("adds 0x4,%0,%0", &addreg1); ! 732: ! 733: /* Do that word. */ ! 734: output_asm_insn (singlemove_string (latehalf), latehalf); ! 735: ! 736: /* Undo the adds we just did. */ ! 737: if (addreg0) ! 738: output_asm_insn ("adds -0x4,%0,%0", &addreg0); ! 739: if (addreg1) ! 740: output_asm_insn ("adds -0x4,%0,%0", &addreg1); ! 741: ! 742: return ""; ! 743: } ! 744: ! 745: static char * ! 746: output_fp_move_double (operands) ! 747: rtx *operands; ! 748: { ! 749: if (FP_REG_P (operands[0])) ! 750: { ! 751: if (FP_REG_P (operands[1])) ! 752: return "fmov.dd %1,%0"; ! 753: if (GET_CODE (operands[1]) == REG) ! 754: { ! 755: output_asm_insn ("ixfr %1,%0", operands); ! 756: operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); ! 757: operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); ! 758: return "ixfr %1,%0"; ! 759: } ! 760: if (operands[1] == dconst0_rtx) ! 761: return "fmov.dd f0,%0"; ! 762: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) ! 763: { ! 764: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 765: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 766: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 767: output_asm_insn ("orh ha%%%m1,r0,r31", operands); ! 768: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 769: cc_status.mdep = XEXP (operands[1], 0); ! 770: return "fld.d l%%%m1(r31),%0"; ! 771: } ! 772: return "fld.d %1,%0"; ! 773: } ! 774: else if (FP_REG_P (operands[1])) ! 775: { ! 776: if (GET_CODE (operands[0]) == REG) ! 777: { ! 778: output_asm_insn ("fxfr %1,%0", operands); ! 779: operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); ! 780: operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); ! 781: return "fxfr %1,%0"; ! 782: } ! 783: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) ! 784: { ! 785: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 786: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 787: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 788: output_asm_insn ("orh ha%%%m0,r0,r31", operands); ! 789: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 790: cc_status.mdep = XEXP (operands[0], 0); ! 791: return "fst.d %1,l%%%m0(r31)"; ! 792: } ! 793: return "fst.d %1,%0"; ! 794: } ! 795: else abort (); ! 796: } ! 797: ! 798: /* Return a REG that occurs in ADDR with coefficient 1. ! 799: ADDR can be effectively incremented by incrementing REG. */ ! 800: ! 801: static rtx ! 802: find_addr_reg (addr) ! 803: rtx addr; ! 804: { ! 805: while (GET_CODE (addr) == PLUS) ! 806: { ! 807: if (GET_CODE (XEXP (addr, 0)) == REG) ! 808: addr = XEXP (addr, 0); ! 809: else if (GET_CODE (XEXP (addr, 1)) == REG) ! 810: addr = XEXP (addr, 1); ! 811: else if (CONSTANT_P (XEXP (addr, 0))) ! 812: addr = XEXP (addr, 1); ! 813: else if (CONSTANT_P (XEXP (addr, 1))) ! 814: addr = XEXP (addr, 0); ! 815: else ! 816: abort (); ! 817: } ! 818: if (GET_CODE (addr) == REG) ! 819: return addr; ! 820: abort (); ! 821: } ! 822: ! 823: /* Return a template for a load instruction with mode MODE and ! 824: arguments from the string ARGS. ! 825: ! 826: This string is in static storage. */ ! 827: ! 828: static char * ! 829: load_opcode (mode, args, reg) ! 830: enum machine_mode mode; ! 831: char *args; ! 832: rtx reg; ! 833: { ! 834: static char buf[30]; ! 835: char *opcode; ! 836: ! 837: switch (mode) ! 838: { ! 839: case QImode: ! 840: opcode = "ld.b"; ! 841: break; ! 842: ! 843: case HImode: ! 844: opcode = "ld.s"; ! 845: break; ! 846: ! 847: case SImode: ! 848: case SFmode: ! 849: if (FP_REG_P (reg)) ! 850: opcode = "fld.l"; ! 851: else ! 852: opcode = "ld.l"; ! 853: break; ! 854: ! 855: case DFmode: ! 856: opcode = "fld.d"; ! 857: break; ! 858: ! 859: default: ! 860: abort (); ! 861: } ! 862: ! 863: sprintf (buf, "%s %s", opcode, args); ! 864: return buf; ! 865: } ! 866: ! 867: /* Return a template for a store instruction with mode MODE and ! 868: arguments from the string ARGS. ! 869: ! 870: This string is in static storage. */ ! 871: ! 872: static char * ! 873: store_opcode (mode, args, reg) ! 874: enum machine_mode mode; ! 875: char *args; ! 876: rtx reg; ! 877: { ! 878: static char buf[30]; ! 879: char *opcode; ! 880: ! 881: switch (mode) ! 882: { ! 883: case QImode: ! 884: opcode = "st.b"; ! 885: break; ! 886: ! 887: case HImode: ! 888: opcode = "st.s"; ! 889: break; ! 890: ! 891: case SImode: ! 892: case SFmode: ! 893: if (FP_REG_P (reg)) ! 894: opcode = "fst.l"; ! 895: else ! 896: opcode = "st.l"; ! 897: break; ! 898: ! 899: case DFmode: ! 900: opcode = "fst.d"; ! 901: break; ! 902: ! 903: default: ! 904: abort (); ! 905: } ! 906: ! 907: sprintf (buf, "%s %s", opcode, args); ! 908: return buf; ! 909: } ! 910: ! 911: /* Output a store-in-memory whose operands are OPERANDS[0,1]. ! 912: OPERANDS[0] is a MEM, and OPERANDS[1] is a reg or zero. ! 913: ! 914: This function returns a template for an insn. ! 915: This is in static storage. ! 916: ! 917: It may also output some insns directly. ! 918: It may alter the values of operands[0] and operands[1]. */ ! 919: ! 920: char * ! 921: output_store (operands) ! 922: rtx *operands; ! 923: { ! 924: enum machine_mode mode = GET_MODE (operands[0]); ! 925: rtx address = XEXP (operands[0], 0); ! 926: char *string; ! 927: ! 928: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 929: cc_status.mdep = address; ! 930: ! 931: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 932: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 933: && address == cc_prev_status.mdep)) ! 934: { ! 935: output_asm_insn ("orh ha%%%m0,r0,r31", operands); ! 936: cc_prev_status.mdep = address; ! 937: } ! 938: ! 939: /* Store zero in two parts when appropriate. */ ! 940: if (mode == DFmode && operands[1] == dconst0_rtx) ! 941: return store_opcode (DFmode, "%r1,l%%%m0(r31)", operands[1]); ! 942: ! 943: /* Code below isn't smart enough to move a doubleword in two parts, ! 944: so use output_move_double to do that in the cases that require it. */ ! 945: if ((mode == DImode || mode == DFmode) ! 946: && ! FP_REG_P (operands[1])) ! 947: return output_move_double (operands); ! 948: ! 949: return store_opcode (mode, "%r1,l%%%m0(r31)", operands[1]); ! 950: } ! 951: ! 952: /* Output a load-from-memory whose operands are OPERANDS[0,1]. ! 953: OPERANDS[0] is a reg, and OPERANDS[1] is a mem. ! 954: ! 955: This function returns a template for an insn. ! 956: This is in static storage. ! 957: ! 958: It may also output some insns directly. ! 959: It may alter the values of operands[0] and operands[1]. */ ! 960: ! 961: char * ! 962: output_load (operands) ! 963: rtx *operands; ! 964: { ! 965: enum machine_mode mode = GET_MODE (operands[0]); ! 966: rtx address = XEXP (operands[1], 0); ! 967: ! 968: /* We don't bother trying to see if we know %hi(address). ! 969: This is because we are doing a load, and if we know the ! 970: %hi value, we probably also know that value in memory. */ ! 971: cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; ! 972: cc_status.mdep = address; ! 973: ! 974: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 975: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 976: && address == cc_prev_status.mdep ! 977: && cc_prev_status.mdep == cc_status.mdep)) ! 978: { ! 979: output_asm_insn ("orh ha%%%m1,r0,r31", operands); ! 980: cc_prev_status.mdep = address; ! 981: } ! 982: ! 983: /* Code below isn't smart enough to move a doubleword in two parts, ! 984: so use output_move_double to do that in the cases that require it. */ ! 985: if ((mode == DImode || mode == DFmode) ! 986: && ! FP_REG_P (operands[0])) ! 987: return output_move_double (operands); ! 988: ! 989: return load_opcode (mode, "l%%%m1(r31),%0", operands[0]); ! 990: } ! 991: ! 992: /* Load the address specified by OPERANDS[3] into the register ! 993: specified by OPERANDS[0]. ! 994: ! 995: OPERANDS[3] may be the result of a sum, hence it could either be: ! 996: ! 997: (1) CONST ! 998: (2) REG ! 999: (2) REG + CONST_INT ! 1000: (3) REG + REG + CONST_INT ! 1001: (4) REG + REG (special case of 3). ! 1002: ! 1003: Note that (3) is not a legitimate address. ! 1004: All cases are handled here. */ ! 1005: ! 1006: void ! 1007: output_load_address (operands) ! 1008: rtx *operands; ! 1009: { ! 1010: rtx base, offset; ! 1011: ! 1012: if (CONSTANT_P (operands[3])) ! 1013: { ! 1014: output_asm_insn ("mov %3,%0", operands); ! 1015: return; ! 1016: } ! 1017: ! 1018: if (REG_P (operands[3])) ! 1019: { ! 1020: if (REGNO (operands[0]) != REGNO (operands[3])) ! 1021: output_asm_insn ("mov %3,%0", operands); ! 1022: return; ! 1023: } ! 1024: ! 1025: if (GET_CODE (operands[3]) != PLUS) ! 1026: abort (); ! 1027: ! 1028: base = XEXP (operands[3], 0); ! 1029: offset = XEXP (operands[3], 1); ! 1030: ! 1031: if (GET_CODE (base) == CONST_INT) ! 1032: { ! 1033: rtx tmp = base; ! 1034: base = offset; ! 1035: offset = tmp; ! 1036: } ! 1037: ! 1038: if (GET_CODE (offset) != CONST_INT) ! 1039: { ! 1040: /* Operand is (PLUS (REG) (REG)). */ ! 1041: base = operands[3]; ! 1042: offset = const0_rtx; ! 1043: } ! 1044: ! 1045: if (REG_P (base)) ! 1046: { ! 1047: operands[6] = base; ! 1048: operands[7] = offset; ! 1049: if (SMALL_INT (offset)) ! 1050: output_asm_insn ("adds %7,%6,%0", operands); ! 1051: else ! 1052: output_asm_insn ("mov %7,%0\n\tadds %0,%6,%0", operands); ! 1053: } ! 1054: else if (GET_CODE (base) == PLUS) ! 1055: { ! 1056: operands[6] = XEXP (base, 0); ! 1057: operands[7] = XEXP (base, 1); ! 1058: operands[8] = offset; ! 1059: ! 1060: if (SMALL_INT (offset)) ! 1061: output_asm_insn ("adds %6,%7,%0\n\tadds %8,%0,%0", operands); ! 1062: else ! 1063: output_asm_insn ("mov %8,%0\n\tadds %0,%6,%0\n\tadds %0,%7,%0", operands); ! 1064: } ! 1065: else ! 1066: abort (); ! 1067: } ! 1068: ! 1069: /* Output code to place a size count SIZE in register REG. ! 1070: Because block moves are pipelined, we don't include the ! 1071: first element in the transfer of SIZE to REG. ! 1072: For this, we subtract ALIGN. (Actually, I think it is not ! 1073: right to subtract on this machine, so right now we don't.) */ ! 1074: ! 1075: static void ! 1076: output_size_for_block_move (size, reg, align) ! 1077: rtx size, reg, align; ! 1078: { ! 1079: rtx xoperands[3]; ! 1080: ! 1081: xoperands[0] = reg; ! 1082: xoperands[1] = size; ! 1083: xoperands[2] = align; ! 1084: ! 1085: #if 1 ! 1086: cc_status.flags &= ~ CC_KNOW_HI_R31; ! 1087: output_asm_insn ("mov %1,%0", xoperands); ! 1088: #else ! 1089: if (GET_CODE (size) == REG) ! 1090: output_asm_insn ("sub %2,%1,%0", xoperands); ! 1091: else ! 1092: { ! 1093: xoperands[1] ! 1094: = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - INTVAL (align)); ! 1095: cc_status.flags &= ~ CC_KNOW_HI_R31; ! 1096: output_asm_insn ("mov %1,%0", xoperands); ! 1097: } ! 1098: #endif ! 1099: } ! 1100: ! 1101: /* Emit code to perform a block move. ! 1102: ! 1103: OPERANDS[0] is the destination. ! 1104: OPERANDS[1] is the source. ! 1105: OPERANDS[2] is the size. ! 1106: OPERANDS[3] is the known safe alignment. ! 1107: OPERANDS[4..6] are pseudos we can safely clobber as temps. */ ! 1108: ! 1109: char * ! 1110: output_block_move (operands) ! 1111: rtx *operands; ! 1112: { ! 1113: /* A vector for our computed operands. Note that load_output_address ! 1114: makes use of (and can clobber) up to the 8th element of this vector. */ ! 1115: rtx xoperands[10]; ! 1116: rtx zoperands[10]; ! 1117: static int movstrsi_label = 0; ! 1118: int i, j; ! 1119: rtx temp1 = operands[4]; ! 1120: rtx alignrtx = operands[3]; ! 1121: int align = INTVAL (alignrtx); ! 1122: ! 1123: xoperands[0] = operands[0]; ! 1124: xoperands[1] = operands[1]; ! 1125: xoperands[2] = temp1; ! 1126: ! 1127: /* We can't move more than four bytes at a time ! 1128: because we have only one register to move them through. */ ! 1129: if (align > 4) ! 1130: { ! 1131: align = 4; ! 1132: alignrtx = gen_rtx (CONST_INT, VOIDmode, 4); ! 1133: } ! 1134: ! 1135: /* Since we clobber untold things, nix the condition codes. */ ! 1136: CC_STATUS_INIT; ! 1137: ! 1138: /* Recognize special cases of block moves. These occur ! 1139: when GNU C++ is forced to treat something as BLKmode ! 1140: to keep it in memory, when its mode could be represented ! 1141: with something smaller. ! 1142: ! 1143: We cannot do this for global variables, since we don't know ! 1144: what pages they don't cross. Sigh. */ ! 1145: if (GET_CODE (operands[2]) == CONST_INT ! 1146: && INTVAL (operands[2]) <= 16 ! 1147: && ! CONSTANT_ADDRESS_P (operands[0]) ! 1148: && ! CONSTANT_ADDRESS_P (operands[1])) ! 1149: { ! 1150: int size = INTVAL (operands[2]); ! 1151: rtx op0 = xoperands[0]; ! 1152: rtx op1 = xoperands[1]; ! 1153: ! 1154: cc_status.flags &= ~CC_KNOW_HI_R31; ! 1155: if (align == 1) ! 1156: { ! 1157: if (memory_address_p (QImode, plus_constant (op0, size)) ! 1158: && memory_address_p (QImode, plus_constant (op1, size))) ! 1159: { ! 1160: for (i = size-1; i >= 0; i--) ! 1161: { ! 1162: xoperands[0] = plus_constant (op0, i); ! 1163: xoperands[1] = plus_constant (op1, i); ! 1164: output_asm_insn ("ld.b %a1,r31\n\tst.b r31,%a0", ! 1165: xoperands); ! 1166: } ! 1167: return ""; ! 1168: } ! 1169: } ! 1170: else if (align == 2) ! 1171: { ! 1172: if (memory_address_p (HImode, plus_constant (op0, size)) ! 1173: && memory_address_p (HImode, plus_constant (op1, size))) ! 1174: { ! 1175: for (i = (size>>1)-1; i >= 0; i--) ! 1176: { ! 1177: xoperands[0] = plus_constant (op0, i * 2); ! 1178: xoperands[1] = plus_constant (op1, i * 2); ! 1179: output_asm_insn ("ld.s %a1,r31\n\tst.s r31,%a0", ! 1180: xoperands); ! 1181: } ! 1182: return ""; ! 1183: } ! 1184: } ! 1185: else ! 1186: { ! 1187: if (memory_address_p (SImode, plus_constant (op0, size)) ! 1188: && memory_address_p (SImode, plus_constant (op1, size))) ! 1189: { ! 1190: for (i = (size>>2)-1; i >= 0; i--) ! 1191: { ! 1192: xoperands[0] = plus_constant (op0, i * 4); ! 1193: xoperands[1] = plus_constant (op1, i * 4); ! 1194: output_asm_insn ("ld.l %a1,r31\n\tst.l r31,%a0", ! 1195: xoperands); ! 1196: } ! 1197: return ""; ! 1198: } ! 1199: } ! 1200: } ! 1201: ! 1202: /* This is the size of the transfer. ! 1203: Either use the register which already contains the size, ! 1204: or use a free register (used by no operands). */ ! 1205: output_size_for_block_move (operands[2], operands[4], alignrtx); ! 1206: ! 1207: #if 0 ! 1208: /* Also emit code to decrement the size value by ALIGN. */ ! 1209: zoperands[0] = operands[0]; ! 1210: zoperands[3] = plus_constant (operands[0], align); ! 1211: output_load_address (zoperands); ! 1212: #endif ! 1213: ! 1214: /* Generate number for unique label. */ ! 1215: ! 1216: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); ! 1217: ! 1218: /* Copy the increment (negative) to a register for bla insn. */ ! 1219: ! 1220: xoperands[4] = gen_rtx (CONST_INT, VOIDmode, - align); ! 1221: xoperands[5] = operands[5]; ! 1222: output_asm_insn ("mov %4,%5", xoperands); ! 1223: ! 1224: xoperands[6] = operands[6]; ! 1225: output_asm_insn ("adds %0,%2,%6", xoperands); ! 1226: ! 1227: /* Now the actual loop. ! 1228: In xoperands, elements 1 and 0 are the input and output vectors. ! 1229: Element 2 is the loop index. Element 5 is the increment. */ ! 1230: ! 1231: if (align == 1) ! 1232: { ! 1233: output_asm_insn ("bla %5,%2,.Lm%3\n\tnop\n.Lm%3:", xoperands); ! 1234: output_asm_insn ("ld.b %1(%2),r31", xoperands); ! 1235: output_asm_insn ("adds %5,%6,%6", xoperands); ! 1236: output_asm_insn ("bla %5,%2,.Lm%3", xoperands); ! 1237: output_asm_insn ("st.b r31,0(%6)", xoperands); ! 1238: } ! 1239: if (align == 2) ! 1240: { ! 1241: output_asm_insn ("bla %5,%2,.Lm%3\n\tnop\n.Lm%3:", xoperands); ! 1242: output_asm_insn ("ld.s %1(%2),r31", xoperands); ! 1243: output_asm_insn ("adds %5,%6,%6", xoperands); ! 1244: output_asm_insn ("bla %5,%2,.Lm%3", xoperands); ! 1245: output_asm_insn ("st.s r31,0(%6)", xoperands); ! 1246: } ! 1247: if (align == 4) ! 1248: { ! 1249: output_asm_insn ("bla %5,%2,.Lm%3\n\tnop\n.Lm%3:", xoperands); ! 1250: output_asm_insn ("ld.l %1(%2),r31", xoperands); ! 1251: output_asm_insn ("adds %5,%6,%6", xoperands); ! 1252: output_asm_insn ("bla %5,%2,.Lm%3", xoperands); ! 1253: output_asm_insn ("st.l r31,0(%6)", xoperands); ! 1254: } ! 1255: ! 1256: return ""; ! 1257: } ! 1258: ! 1259: /* Output a delayed branch insn with the delay insn in its ! 1260: branch slot. The delayed branch insn template is in TEMPLATE, ! 1261: with operands OPERANDS. The insn in its delay slot is INSN. ! 1262: ! 1263: As a special case, since we know that all memory transfers are via ! 1264: ld/st insns, if we see a (MEM (SYMBOL_REF ...)) we divide the memory ! 1265: reference around the branch as ! 1266: ! 1267: orh ha%x,r0,r31 ! 1268: b ... ! 1269: ld/st l%x(r31),... ! 1270: ! 1271: As another special case, we handle loading (SYMBOL_REF ...) and ! 1272: other large constants around branches as well: ! 1273: ! 1274: orh h%x,r0,%0 ! 1275: b ... ! 1276: or l%x,%0,%1 ! 1277: ! 1278: */ ! 1279: ! 1280: char * ! 1281: output_delayed_branch (template, operands, insn) ! 1282: char *template; ! 1283: rtx *operands; ! 1284: rtx insn; ! 1285: { ! 1286: extern rtx recog_operand[]; ! 1287: rtx src = XVECEXP (PATTERN (insn), 0, 1); ! 1288: rtx dest = XVECEXP (PATTERN (insn), 0, 0); ! 1289: ! 1290: if (GET_CODE (src) == SYMBOL_REF || GET_CODE (src) == CONST ! 1291: || (GET_CODE (src) == CONST_INT ! 1292: && !(SMALL_INT (src) || (INTVAL (src) & 0x3ff) == 0))) ! 1293: { ! 1294: rtx xoperands[2]; ! 1295: xoperands[0] = dest; ! 1296: xoperands[1] = src; ! 1297: ! 1298: /* Output the `orh' insn. */ ! 1299: output_asm_insn ("orh h%%%1,r0,%0", xoperands); ! 1300: ! 1301: /* Output the branch instruction next. */ ! 1302: output_asm_insn (template, operands); ! 1303: ! 1304: /* Now output the `or' insn. */ ! 1305: output_asm_insn ("or l%%%1,%0,%0", xoperands); ! 1306: } ! 1307: else if ((GET_CODE (src) == MEM ! 1308: && CONSTANT_ADDRESS_P (XEXP (src, 0))) ! 1309: || (GET_CODE (dest) == MEM ! 1310: && CONSTANT_ADDRESS_P (XEXP (dest, 0)))) ! 1311: { ! 1312: rtx xoperands[2]; ! 1313: char *split_template; ! 1314: xoperands[0] = dest; ! 1315: xoperands[1] = src; ! 1316: ! 1317: /* Output the `orh' insn. */ ! 1318: if (GET_CODE (src) == MEM) ! 1319: { ! 1320: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 1321: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 1322: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 1323: output_asm_insn ("orh ha%%%m1,r0,r31", xoperands); ! 1324: split_template = load_opcode (GET_MODE (dest), ! 1325: "l%%%m1(r31),%0", src); ! 1326: } ! 1327: else ! 1328: { ! 1329: if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) ! 1330: && (cc_prev_status.flags & CC_HI_R31_ADJ) ! 1331: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 1332: output_asm_insn ("orh ha%%%m0,r0,r31", xoperands); ! 1333: split_template = store_opcode (GET_MODE (dest), ! 1334: "%r1,l%%%m0(r31)", src); ! 1335: } ! 1336: ! 1337: /* Output the branch instruction next. */ ! 1338: output_asm_insn (template, operands); ! 1339: ! 1340: /* Now output the load or store. ! 1341: No need to do a CC_STATUS_INIT, because we are branching anyway. */ ! 1342: output_asm_insn (split_template, xoperands); ! 1343: } ! 1344: else ! 1345: { ! 1346: extern char *insn_template[]; ! 1347: extern char *(*insn_outfun[])(); ! 1348: extern int insn_n_operands[]; ! 1349: extern rtx alter_subreg(); ! 1350: int insn_code_number; ! 1351: rtx pat = gen_rtx (SET, VOIDmode, dest, src); ! 1352: rtx delay_insn = gen_rtx (INSN, VOIDmode, 0, 0, 0, pat, -1, 0, 0); ! 1353: int i; ! 1354: ! 1355: /* Output the branch instruction first. */ ! 1356: output_asm_insn (template, operands); ! 1357: ! 1358: /* Now recognize the insn which we put in its delay slot. ! 1359: We must do this after outputing the branch insn, ! 1360: since operands may just be a pointer to `recog_operand'. */ ! 1361: insn_code_number = recog (pat, delay_insn); ! 1362: if (insn_code_number == -1) ! 1363: abort (); ! 1364: ! 1365: for (i = 0; i < insn_n_operands[insn_code_number]; i++) ! 1366: { ! 1367: if (GET_CODE (recog_operand[i]) == SUBREG) ! 1368: recog_operand[i] = alter_subreg (recog_operand[i]); ! 1369: } ! 1370: ! 1371: /* Now get the template for what this insn would ! 1372: have been, without the branch. Its operands are ! 1373: exactly the same as they would be, so we don't ! 1374: need to do an insn_extract. */ ! 1375: template = insn_template[insn_code_number]; ! 1376: if (template == 0) ! 1377: template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); ! 1378: output_asm_insn (template, recog_operand); ! 1379: } ! 1380: CC_STATUS_INIT; ! 1381: return ""; ! 1382: } ! 1383: ! 1384: /* Output a newly constructed insn DELAY_INSN. */ ! 1385: char * ! 1386: output_delay_insn (delay_insn) ! 1387: rtx delay_insn; ! 1388: { ! 1389: char *template; ! 1390: extern rtx recog_operand[]; ! 1391: extern char call_used_regs[]; ! 1392: extern char *insn_template[]; ! 1393: extern int insn_n_operands[]; ! 1394: extern char *(*insn_outfun[])(); ! 1395: extern rtx alter_subreg(); ! 1396: int insn_code_number; ! 1397: extern int insn_n_operands[]; ! 1398: int i; ! 1399: ! 1400: /* Now recognize the insn which we put in its delay slot. ! 1401: We must do this after outputing the branch insn, ! 1402: since operands may just be a pointer to `recog_operand'. */ ! 1403: insn_code_number = recog_memoized (delay_insn); ! 1404: if (insn_code_number == -1) ! 1405: abort (); ! 1406: ! 1407: /* Extract the operands of this delay insn. */ ! 1408: INSN_CODE (delay_insn) = insn_code_number; ! 1409: insn_extract (delay_insn); ! 1410: ! 1411: /* It is possible that this insn has not been properly scaned by final ! 1412: yet. If this insn's operands don't appear in the peephole's ! 1413: actual operands, then they won't be fixed up by final, so we ! 1414: make sure they get fixed up here. -- This is a kludge. */ ! 1415: for (i = 0; i < insn_n_operands[insn_code_number]; i++) ! 1416: { ! 1417: if (GET_CODE (recog_operand[i]) == SUBREG) ! 1418: recog_operand[i] = alter_subreg (recog_operand[i]); ! 1419: } ! 1420: ! 1421: #ifdef REGISTER_CONSTRAINTS ! 1422: if (! constrain_operands (insn_code_number)) ! 1423: abort (); ! 1424: #endif ! 1425: ! 1426: cc_prev_status = cc_status; ! 1427: ! 1428: /* Update `cc_status' for this instruction. ! 1429: The instruction's output routine may change it further. ! 1430: If the output routine for a jump insn needs to depend ! 1431: on the cc status, it should look at cc_prev_status. */ ! 1432: ! 1433: NOTICE_UPDATE_CC (PATTERN (delay_insn), delay_insn); ! 1434: ! 1435: /* Now get the template for what this insn would ! 1436: have been, without the branch. */ ! 1437: ! 1438: template = insn_template[insn_code_number]; ! 1439: if (template == 0) ! 1440: template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); ! 1441: output_asm_insn (template, recog_operand); ! 1442: return ""; ! 1443: } ! 1444:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.