|
|
1.1 root 1: /* Subroutines for insn-output.c for Sun SPARC. 1.1.1.9 ! root 2: Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. 1.1 root 3: Contributed by Michael Tiemann ([email protected]) 4: 5: This file is part of GNU CC. 6: 7: GNU CC is distributed in the hope that it will be useful, 8: but WITHOUT ANY WARRANTY. No author or distributor 9: accepts responsibility to anyone for the consequences of using it 10: or for whether it serves any particular purpose or works at all, 11: unless he says so in writing. Refer to the GNU CC General Public 12: License for full details. 13: 14: Everyone is granted permission to copy, modify and redistribute 15: GNU CC, but only under the conditions described in the 16: GNU CC General Public License. A copy of this license is 17: supposed to have been given to you along with GNU CC so you 18: can know your rights and responsibilities. It should be in a 19: file named COPYING. Among other things, the copyright notice 20: and this notice must be preserved on all copies. */ 21: 22: /* Global variables for machine-dependend things. */ 23: 24: /* This should go away if we pass floats to regs via 1.1.1.4 root 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!). */ 1.1 root 27: extern int frame_pointer_needed; 28: 29: static rtx find_addr_reg (); 30: 1.1.1.9 ! root 31: rtx next_real_insn_no_labels (); ! 32: 1.1.1.8 root 33: /* Return non-zero only if OP is a register of mode MODE, 34: or const0_rtx. */ 35: int 36: reg_or_0_operand (op, mode) 1.1.1.7 root 37: rtx op; 38: enum machine_mode mode; 39: { 1.1.1.8 root 40: return (op == const0_rtx || register_operand (op, mode)); 1.1.1.7 root 41: } 42: 1.1.1.9 ! root 43: /* Return non-zero if this pattern, can be evaluated safely, even if it ! 44: was not asked for. */ ! 45: int ! 46: safe_insn_src_p (op, mode) ! 47: rtx op; ! 48: enum machine_mode mode; ! 49: { ! 50: /* Just experimenting. */ ! 51: ! 52: /* No floating point src is safe if it contains an arithmetic ! 53: operation, since that operation may trap. */ ! 54: switch (GET_CODE (op)) ! 55: { ! 56: case CONST_INT: ! 57: case LABEL_REF: ! 58: case SYMBOL_REF: ! 59: case CONST: ! 60: return 1; ! 61: ! 62: case REG: ! 63: return 1; ! 64: ! 65: case MEM: ! 66: return CONSTANT_ADDRESS_P (XEXP (op, 0)); ! 67: ! 68: /* We never need to negate or complement constants. */ ! 69: case NEG: ! 70: return (mode != SFmode && mode != DFmode); ! 71: case NOT: ! 72: return 1; ! 73: ! 74: case MINUS: ! 75: case PLUS: ! 76: return (mode != SFmode && mode != DFmode); ! 77: case AND: ! 78: case IOR: ! 79: case XOR: ! 80: case LSHIFT: ! 81: case ASHIFT: ! 82: case ASHIFTRT: ! 83: case LSHIFTRT: ! 84: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) ! 85: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) ! 86: return 0; ! 87: return 1; ! 88: ! 89: default: ! 90: return 0; ! 91: } ! 92: } ! 93: ! 94: /* Return 1 if REG is clobbered in IN. ! 95: Return 0 if REG is used in IN (other than being clobbered). ! 96: Return 2 if REG does not appear in IN. */ ! 97: ! 98: static int ! 99: reg_clobbered_p (reg, in) ! 100: rtx reg; ! 101: rtx in; ! 102: { ! 103: register char *fmt; ! 104: register int i, result = 0; ! 105: ! 106: register enum rtx_code code; ! 107: ! 108: if (in == 0) ! 109: return 2; ! 110: ! 111: code = GET_CODE (in); ! 112: ! 113: switch (code) ! 114: { ! 115: /* Let these fail out quickly. */ ! 116: case CONST_INT: ! 117: case SYMBOL_REF: ! 118: case CONST: ! 119: return 2; ! 120: ! 121: case SUBREG: ! 122: if (SUBREG_WORD (in) != 0) ! 123: in = gen_rtx (REG, SImode, REGNO (SUBREG_REG (in)) + SUBREG_WORD (in)); ! 124: else ! 125: in = SUBREG_REG (in); ! 126: ! 127: case REG: ! 128: if (in == reg ! 129: || refers_to_regno_p (REGNO (reg), ! 130: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 131: in, 0)) ! 132: return 0; ! 133: return 2; ! 134: ! 135: case SET: ! 136: if (SET_SRC (in) == reg ! 137: || refers_to_regno_p (REGNO (reg), ! 138: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 139: SET_SRC (in), 0)) ! 140: return 0; ! 141: ! 142: if (SET_DEST (in) == reg) ! 143: return 1; ! 144: ! 145: if (refers_to_regno_p (REGNO (reg), ! 146: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 147: SET_DEST (in), 0)) ! 148: if (GET_CODE (SET_DEST (in)) == REG ! 149: || GET_CODE (SET_DEST (in)) == SUBREG) ! 150: return 1; ! 151: else ! 152: return 0; ! 153: return 2; ! 154: ! 155: case USE: ! 156: if (XEXP (in, 0) == reg ! 157: || refers_to_regno_p (REGNO (reg), ! 158: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 159: XEXP (in, 0), 0)) ! 160: return 0; ! 161: return 2; ! 162: ! 163: case CLOBBER: ! 164: if (XEXP (in, 0) == reg) ! 165: return 1; ! 166: /* If the CLOBBER expression is a SUBREG, accept that as a ! 167: clobber. But if it is some expression based on this register, ! 168: that is like a USE as far as this register is concerned, ! 169: so we won't take it. */ ! 170: if (refers_to_regno_p (REGNO (reg), ! 171: REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), ! 172: XEXP (in, 0), 0)) ! 173: if (GET_CODE (XEXP (in, 0)) == REG ! 174: || GET_CODE (XEXP (in, 0)) == SUBREG) ! 175: return 1; ! 176: else ! 177: return 0; ! 178: return 2; ! 179: } ! 180: ! 181: fmt = GET_RTX_FORMAT (code); ! 182: ! 183: result = 2; ! 184: ! 185: for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) ! 186: { ! 187: if (fmt[i] == 'E') ! 188: { ! 189: register int j; ! 190: for (j = XVECLEN (in, i) - 1; j >= 0; j--) ! 191: switch (reg_clobbered_p (reg, XVECEXP (in, i, j))) ! 192: { ! 193: case 0: ! 194: return 0; ! 195: case 2: ! 196: continue; ! 197: case 1: ! 198: result = 1; ! 199: break; ! 200: } ! 201: } ! 202: else if (fmt[i] == 'e') ! 203: switch (reg_clobbered_p (reg, XEXP (in, i))) ! 204: { ! 205: case 0: ! 206: return 0; ! 207: case 2: ! 208: continue; ! 209: case 1: ! 210: result = 1; ! 211: break; ! 212: } ! 213: } ! 214: return result; ! 215: } ! 216: ! 217: /* Return non-zero if OP can be written to without screwing up ! 218: GCC's model of what's going on. It is assumed that this operand ! 219: appears in the dest position of a SET insn in a conditional ! 220: branch's delay slot. AFTER is the label to start looking from. */ ! 221: int ! 222: operand_clobbered_before_used_after (op, after) ! 223: rtx op; ! 224: rtx after; ! 225: { ! 226: extern char call_used_regs[]; ! 227: ! 228: /* Just experimenting. */ ! 229: if (GET_CODE (op) == CC0) ! 230: return 1; ! 231: if (GET_CODE (op) == REG) ! 232: { ! 233: rtx insn; ! 234: ! 235: if (op == stack_pointer_rtx) ! 236: return 0; ! 237: ! 238: for (insn = NEXT_INSN (after); insn; insn = NEXT_INSN (insn)) ! 239: { ! 240: if (GET_CODE (insn) == NOTE) ! 241: continue; ! 242: if (GET_CODE (insn) == INSN ! 243: || GET_CODE (insn) == JUMP_INSN ! 244: || GET_CODE (insn) == CALL_INSN) ! 245: { ! 246: switch (reg_clobbered_p (op, PATTERN (insn))) ! 247: { ! 248: case 0: ! 249: return 0; ! 250: case 2: ! 251: break; ! 252: case 1: ! 253: return 1; ! 254: } ! 255: if (dead_or_set_p (insn, op)) ! 256: return 1; ! 257: } ! 258: else if (GET_CODE (insn) == CODE_LABEL) ! 259: return 0; ! 260: if (GET_CODE (insn) == JUMP_INSN) ! 261: { ! 262: if (condjump_p (insn)) ! 263: return 0; ! 264: /* This is a jump insn which has already ! 265: been mangled. We can't tell what it does. */ ! 266: if (GET_CODE (PATTERN (insn)) == PARALLEL) ! 267: return 0; ! 268: if (! JUMP_LABEL (insn)) ! 269: return 0; ! 270: /* Keep following jumps. */ ! 271: insn = JUMP_LABEL (insn); ! 272: } ! 273: } ! 274: return 1; ! 275: } ! 276: ! 277: /* In both of these cases, the first insn executed ! 278: for this op will be a sethi %hi(whatever),%g1, ! 279: which is tolerable. */ ! 280: if (GET_CODE (op) == MEM) ! 281: return (CONSTANT_ADDRESS_P (XEXP (op, 0))); ! 282: ! 283: return 0; ! 284: } ! 285: 1.1.1.4 root 286: /* Return non-zero if this pattern, as a source to a "SET", 287: is known to yield an instruction of unit size. */ 288: int 289: single_insn_src_p (op, mode) 290: rtx op; 291: enum machine_mode mode; 1.1 root 292: { 1.1.1.4 root 293: switch (GET_CODE (op)) 294: { 295: case CONST_INT: 1.1.1.9 ! root 296: #if 1 ! 297: /* This is not always a single insn src, technically, ! 298: but output_delayed_branch knows how to deal with it. */ ! 299: return 1; ! 300: #else 1.1.1.4 root 301: if (SMALL_INT (op)) 302: return 1; 1.1.1.9 ! root 303: /* We can put this set insn into delay slot, because this is one ! 304: insn; 'sethi'. */ ! 305: if ((INTVAL (op) & 0x3ff) == 0) ! 306: return 1; ! 307: ! 308: /* This is not a single insn src, technically, ! 309: but output_delayed_branch knows how to deal with it. */ ! 310: return 1; ! 311: #endif ! 312: ! 313: #if 1 ! 314: case SYMBOL_REF: ! 315: /* This is not a single insn src, technically, ! 316: but output_delayed_branch knows how to deal with it. */ ! 317: return 1; ! 318: #else 1.1.1.4 root 319: return 0; 1.1.1.9 ! root 320: #endif 1.1.1.4 root 321: 322: case REG: 1.1.1.7 root 323: return 1; 324: 1.1.1.4 root 325: case MEM: 1.1.1.8 root 326: #if 0 327: /* This is not a single insn src, technically, 1.1.1.9 ! root 328: but output_delayed_branch knows how to deal with it. */ 1.1.1.7 root 329: if (GET_CODE (XEXP (op, 0)) == SYMBOL_REF) 330: return 0; 1.1.1.8 root 331: #endif 1.1.1.4 root 332: return 1; 333: 334: /* We never need to negate or complement constants. */ 335: case NEG: 1.1.1.9 ! root 336: return (mode != DFmode); ! 337: case NOT: 1.1.1.4 root 338: return 1; 339: 340: case MINUS: 1.1.1.9 ! root 341: /* If the target is cc0, then these insns will take ! 342: two insns (one being a nop). */ ! 343: return (mode != SFmode && mode != DFmode); ! 344: case PLUS: 1.1.1.4 root 345: case AND: 346: case IOR: 347: case XOR: 348: case LSHIFT: 349: case ASHIFT: 350: case ASHIFTRT: 351: case LSHIFTRT: 352: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) 353: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) 354: return 0; 355: return 1; 356: 357: case SUBREG: 358: if (SUBREG_WORD (op) != 0) 359: return 0; 360: return single_insn_src_p (SUBREG_REG (op), mode); 361: 362: case SIGN_EXTEND: 363: case ZERO_EXTEND: 1.1.1.9 ! root 364: /* Lazy... could check for more cases. */ ! 365: if (GET_CODE (XEXP (op, 0)) == MEM ! 366: && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0))) ! 367: return 1; ! 368: return 0; ! 369: ! 370: /* Not doing floating point, since they probably ! 371: take longer than the branch slot they might fill. */ ! 372: case FLOAT_EXTEND: ! 373: case FLOAT_TRUNCATE: ! 374: case FLOAT: ! 375: case FIX: ! 376: case UNSIGNED_FLOAT: ! 377: case UNSIGNED_FIX: ! 378: return 0; ! 379: ! 380: default: ! 381: return 0; ! 382: } ! 383: } ! 384: ! 385: /* Nonzero only if this *really* is a single insn operand. */ ! 386: int ! 387: strict_single_insn_op_p (op, mode) ! 388: rtx op; ! 389: enum machine_mode mode; ! 390: { ! 391: if (mode == VOIDmode) ! 392: mode = GET_MODE (op); ! 393: ! 394: switch (GET_CODE (op)) ! 395: { ! 396: case CC0: ! 397: return 1; ! 398: ! 399: case CONST_INT: ! 400: if (SMALL_INT (op)) ! 401: return 1; ! 402: /* We can put this set insn into delay slot, because this is one ! 403: insn; 'sethi'. */ ! 404: if ((INTVAL (op) & 0x3ff) == 0) ! 405: return 1; ! 406: return 0; ! 407: ! 408: case SYMBOL_REF: ! 409: return 0; ! 410: ! 411: case REG: ! 412: return (mode != DFmode && mode != DImode); ! 413: ! 414: case MEM: ! 415: if (! CONSTANT_ADDRESS_P (XEXP (op, 0))) ! 416: return (mode != DFmode && mode != DImode); ! 417: return 0; ! 418: ! 419: /* We never need to negate or complement constants. */ ! 420: case NEG: ! 421: return (mode != DFmode); ! 422: case NOT: ! 423: return 1; ! 424: ! 425: case MINUS: ! 426: /* If the target is cc0, then these insns will take ! 427: two insns (one being a nop). */ ! 428: return (mode != SFmode && mode != DFmode); ! 429: case PLUS: ! 430: case AND: ! 431: case IOR: ! 432: case XOR: ! 433: case LSHIFT: ! 434: case ASHIFT: ! 435: case ASHIFTRT: ! 436: case LSHIFTRT: ! 437: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) ! 438: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) ! 439: return 0; ! 440: return 1; ! 441: ! 442: case SUBREG: ! 443: if (SUBREG_WORD (op) != 0) ! 444: return 0; ! 445: return strict_single_insn_op_p (SUBREG_REG (op), mode); ! 446: ! 447: case SIGN_EXTEND: ! 448: case ZERO_EXTEND: ! 449: if (GET_CODE (XEXP (op, 0)) == MEM ! 450: && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0))) ! 451: return 1; 1.1.1.4 root 452: return 0; 453: 454: /* Not doing floating point, since they probably 455: take longer than the branch slot they might fill. */ 456: case FLOAT_EXTEND: 457: case FLOAT_TRUNCATE: 458: case FLOAT: 459: case FIX: 460: case UNSIGNED_FLOAT: 461: case UNSIGNED_FIX: 462: return 0; 463: 464: default: 465: return 0; 466: } 1.1 root 467: } 1.1.1.4 root 468: 1.1.1.9 ! root 469: /* Return truth value of whether OP is a relational operator. */ ! 470: int ! 471: relop (op, mode) ! 472: rtx op; ! 473: enum machine_mode mode; ! 474: { ! 475: switch (GET_CODE (op)) ! 476: { ! 477: case EQ: ! 478: case NE: ! 479: case GT: ! 480: case GE: ! 481: case LT: ! 482: case LE: ! 483: case GTU: ! 484: case GEU: ! 485: case LTU: ! 486: case LEU: ! 487: return 1; ! 488: } ! 489: return 0; ! 490: } ! 491: ! 492: /* Return truth value of wheterh OP is EQ or NE. */ ! 493: int ! 494: eq_or_neq (op, mode) ! 495: rtx op; ! 496: enum machine_mode mode; ! 497: { ! 498: return (GET_CODE (op) == EQ || GET_CODE (op) == NE); ! 499: } ! 500: 1.1.1.5 root 501: /* Return truth value of whether OP can be used as an operands in a three 502: address arithmetic insn (such as add %o1,7,%l2) of mode MODE. */ 1.1 root 503: 504: int 505: arith_operand (op, mode) 506: rtx op; 507: enum machine_mode mode; 508: { 509: return (register_operand (op, mode) 510: || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); 511: } 512: 1.1.1.5 root 513: /* Return truth value of whether OP can be used as an operand in a two 514: address arithmetic insn (such as set 123456,%o4) of mode MODE. */ 1.1.1.4 root 515: 1.1 root 516: int 517: arith32_operand (op, mode) 518: rtx op; 519: enum machine_mode mode; 520: { 521: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); 522: } 523: 1.1.1.5 root 524: /* Return truth value of whether OP is a integer which fits the 525: range constraining immediate operands in three-address insns. */ 526: 1.1.1.3 root 527: int 528: small_int (op, mode) 529: rtx op; 530: enum machine_mode mode; 531: { 532: return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); 533: } 534: 1.1 root 535: /* Return the best assembler insn template 536: for moving operands[1] into operands[0] as a fullword. */ 537: 538: static char * 539: singlemove_string (operands) 540: rtx *operands; 541: { 542: if (GET_CODE (operands[0]) == MEM) 1.1.1.8 root 543: { 544: if (GET_CODE (operands[1]) != MEM) 545: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) 546: { 547: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) 548: && cc_prev_status.mdep == XEXP (operands[0], 0))) 549: output_asm_insn ("sethi %%hi(%m0),%%g1", operands); 550: cc_status.flags |= CC_KNOW_HI_G1; 551: cc_status.mdep = XEXP (operands[0], 0); 552: return "st %1,[%%lo(%m0)+%%g1]"; 553: } 554: else 555: return "st %r1,%0"; 556: else 557: { 558: rtx xoperands[2]; 559: 560: cc_status.flags &= ~CC_F0_IS_0; 561: xoperands[0] = gen_rtx (REG, SFmode, 32); 562: xoperands[1] = operands[1]; 563: output_asm_insn (singlemove_string (xoperands), xoperands); 564: xoperands[1] = xoperands[0]; 565: xoperands[0] = operands[0]; 566: output_asm_insn (singlemove_string (xoperands), xoperands); 567: return ""; 568: } 569: } 1.1 root 570: if (GET_CODE (operands[1]) == MEM) 1.1.1.8 root 571: { 572: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) 573: { 574: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) 575: && cc_prev_status.mdep == XEXP (operands[1], 0))) 576: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); 577: cc_status.flags |= CC_KNOW_HI_G1; 578: cc_status.mdep = XEXP (operands[1], 0); 579: return "ld [%%lo(%m1)+%%g1],%0"; 580: } 581: return "ld %1,%0"; 582: } 1.1.1.3 root 583: return "mov %1,%0"; 1.1 root 584: } 585: 586: /* Output assembler code to perform a doubleword move insn 587: with operands OPERANDS. */ 588: 589: char * 590: output_move_double (operands) 591: rtx *operands; 592: { 593: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; 594: rtx latehalf[2]; 595: rtx addreg0 = 0, addreg1 = 0; 596: 597: /* First classify both operands. */ 598: 599: if (REG_P (operands[0])) 600: optype0 = REGOP; 601: else if (offsetable_memref_p (operands[0])) 602: optype0 = OFFSOP; 603: else if (GET_CODE (operands[0]) == MEM) 604: optype0 = MEMOP; 605: else 606: optype0 = RNDOP; 607: 608: if (REG_P (operands[1])) 609: optype1 = REGOP; 610: else if (CONSTANT_P (operands[1]) 611: || GET_CODE (operands[1]) == CONST_DOUBLE) 612: optype1 = CNSTOP; 613: else if (offsetable_memref_p (operands[1])) 614: optype1 = OFFSOP; 615: else if (GET_CODE (operands[1]) == MEM) 1.1.1.7 root 616: optype1 = MEMOP; 1.1 root 617: else 618: optype1 = RNDOP; 619: 620: /* Check for the cases that the operand constraints are not 621: supposed to allow to happen. Abort if we get one, 622: because generating code for these cases is painful. */ 623: 624: if (optype0 == RNDOP || optype1 == RNDOP) 625: abort (); 626: 627: /* If an operand is an unoffsettable memory ref, find a register 628: we can increment temporarily to make it refer to the second word. */ 629: 630: if (optype0 == MEMOP) 631: addreg0 = find_addr_reg (operands[0]); 632: 633: if (optype1 == MEMOP) 634: addreg1 = find_addr_reg (operands[1]); 635: 636: /* Ok, we can do one word at a time. 637: Normally we do the low-numbered word first, 638: but if either operand is autodecrementing then we 639: do the high-numbered word first. 640: 641: In either case, set up in LATEHALF the operands to use 642: for the high-numbered word and in some cases alter the 643: operands in OPERANDS to be suitable for the low-numbered word. */ 644: 645: if (optype0 == REGOP) 646: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); 647: else if (optype0 == OFFSOP) 648: latehalf[0] = adj_offsetable_operand (operands[0], 4); 649: else 650: latehalf[0] = operands[0]; 651: 652: if (optype1 == REGOP) 653: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); 654: else if (optype1 == OFFSOP) 655: latehalf[1] = adj_offsetable_operand (operands[1], 4); 656: else if (optype1 == CNSTOP) 657: { 658: if (CONSTANT_P (operands[1])) 659: latehalf[1] = const0_rtx; 660: else if (GET_CODE (operands[1]) == CONST_DOUBLE) 661: { 1.1.1.9 ! root 662: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 663: CONST_DOUBLE_HIGH (operands[1])); ! 664: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 665: CONST_DOUBLE_LOW (operands[1])); 1.1 root 666: } 667: } 668: else 669: latehalf[1] = operands[1]; 670: 671: /* If the first move would clobber the source of the second one, 1.1.1.3 root 672: do them in the other order. 673: 674: RMS says "This happens only for registers; 1.1 root 675: such overlap can't happen in memory unless the user explicitly 1.1.1.3 root 676: sets it up, and that is an undefined circumstance." 677: 678: but it happens on the sparc when loading parameter registers, 679: so I am going to define that circumstance, and make it work 680: as expected. */ 681: 682: /* Easy case: try moving both words at once. */ 1.1.1.8 root 683: /* First check for moving between an even/odd register pair 684: and a memory location. */ 685: if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP 1.1.1.3 root 686: && (REGNO (operands[0]) & 1) == 0) 1.1.1.8 root 687: || (optype0 != REGOP && optype1 != CNSTOP && optype1 == REGOP 1.1.1.3 root 688: && (REGNO (operands[1]) & 1) == 0)) 689: { 690: rtx op1, op2; 691: rtx base = 0, offset = const0_rtx; 1.1 root 692: 1.1.1.8 root 693: /* OP1 gets the register pair, and OP2 gets the memory address. */ 1.1.1.3 root 694: if (optype0 == REGOP) 695: op1 = operands[0], op2 = XEXP (operands[1], 0); 696: else 697: op1 = operands[1], op2 = XEXP (operands[0], 0); 698: 1.1.1.8 root 699: /* Now see if we can trust the address to be 8-byte aligned. */ 1.1.1.4 root 700: /* Trust global variables. */ 1.1.1.8 root 701: if (CONSTANT_ADDRESS_P (op2)) 1.1.1.4 root 702: { 1.1.1.8 root 703: operands[0] = op1; 704: operands[1] = op2; 705: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) 706: && cc_prev_status.mdep == op2)) 1.1.1.9 ! root 707: output_asm_insn ("sethi %%hi(%1),%%g1", operands); 1.1.1.8 root 708: cc_status.flags |= CC_KNOW_HI_G1; 709: cc_status.mdep = op2; 1.1.1.4 root 710: if (op1 == operands[0]) 1.1.1.9 ! root 711: return "ldd [%%lo(%1)+%%g1],%0"; 1.1.1.4 root 712: else 1.1.1.9 ! root 713: return "std [%%lo(%1)+%%g1],%0"; 1.1.1.4 root 714: } 1.1.1.3 root 715: 1.1.1.4 root 716: if (GET_CODE (op2) == PLUS) 717: { 718: if (GET_CODE (XEXP (op2, 0)) == REG) 719: base = XEXP (op2, 0), offset = XEXP (op2, 1); 720: else if (GET_CODE (XEXP (op2, 1)) == REG) 721: base = XEXP (op2, 1), offset = XEXP (op2, 0); 722: } 723: 724: /* Trust round enough offsets from the stack or frame pointer. */ 1.1.1.3 root 725: if (base 1.1.1.4 root 726: && (REGNO (base) == FRAME_POINTER_REGNUM 727: || REGNO (base) == STACK_POINTER_REGNUM)) 728: { 729: if (GET_CODE (offset) == CONST_INT 730: && (INTVAL (offset) & 0x7) == 0) 731: { 732: if (op1 == operands[0]) 733: return "ldd %1,%0"; 734: else 735: return "std %1,%0"; 736: } 737: } 738: else 739: { 1.1.1.8 root 740: /* We know structs not on the stack are properly aligned. 741: Since a double asks for 8-byte alignment, 742: we know it must have got that if it is in a struct. 743: But a DImode need not be 8-byte aligned, because it could be a 744: struct containing two ints or pointers. */ 745: if (GET_CODE (operands[1]) == MEM && GET_MODE (operands[1]) == DFmode 746: && MEM_IN_STRUCT_P (operands[1])) 1.1.1.4 root 747: return "ldd %1,%0"; 1.1.1.8 root 748: else if (GET_CODE (operands[0]) == MEM 749: && GET_MODE (operands[0]) == DFmode 750: && MEM_IN_STRUCT_P (operands[0])) 1.1.1.4 root 751: return "std %1,%0"; 752: } 1.1.1.3 root 753: } 1.1.1.4 root 754: 1.1 root 755: if (optype0 == REGOP && optype1 == REGOP 756: && REGNO (operands[0]) == REGNO (latehalf[1])) 757: { 758: /* Make any unoffsetable addresses point at high-numbered word. */ 759: if (addreg0) 760: output_asm_insn ("add %0,0x4,%0", &addreg0); 761: if (addreg1) 762: output_asm_insn ("add %0,0x4,%0", &addreg1); 763: 764: /* Do that word. */ 765: output_asm_insn (singlemove_string (latehalf), latehalf); 766: 767: /* Undo the adds we just did. */ 768: if (addreg0) 769: output_asm_insn ("add %0,-0x4,%0", &addreg0); 770: if (addreg1) 771: output_asm_insn ("add %0,-0x4,%0", &addreg0); 772: 773: /* Do low-numbered word. */ 774: return singlemove_string (operands); 775: } 1.1.1.3 root 776: else if (optype0 == REGOP && optype1 != REGOP 1.1.1.8 root 777: && reg_overlap_mentioned_p (operands[0], operands[1])) 1.1.1.3 root 778: { 779: /* Do the late half first. */ 780: output_asm_insn (singlemove_string (latehalf), latehalf); 781: /* Then clobber. */ 782: return singlemove_string (operands); 783: } 1.1 root 784: 785: /* Normal case: do the two words, low-numbered first. */ 786: 787: output_asm_insn (singlemove_string (operands), operands); 788: 789: /* Make any unoffsetable addresses point at high-numbered word. */ 790: if (addreg0) 791: output_asm_insn ("add %0,0x4,%0", &addreg0); 792: if (addreg1) 793: output_asm_insn ("add %0,0x4,%0", &addreg1); 794: 795: /* Do that word. */ 796: output_asm_insn (singlemove_string (latehalf), latehalf); 797: 798: /* Undo the adds we just did. */ 799: if (addreg0) 800: output_asm_insn ("add %0,-0x4,%0", &addreg0); 801: if (addreg1) 802: output_asm_insn ("add %0,-0x4,%0", &addreg1); 803: 804: return ""; 805: } 806: 807: static char * 808: output_fp_move_double (operands) 809: rtx *operands; 810: { 811: if (FP_REG_P (operands[0])) 812: { 813: if (FP_REG_P (operands[1])) 814: { 815: output_asm_insn ("fmovs %1,%0", operands); 816: operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); 817: operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); 818: return "fmovs %1,%0"; 819: } 820: if (GET_CODE (operands[1]) == REG) 821: { 1.1.1.3 root 822: if ((REGNO (operands[1]) & 1) == 0) 823: return "std %1,[%%fp-8]\n\tldd [%%fp-8],%0"; 824: else 825: { 826: rtx xoperands[3]; 827: xoperands[0] = operands[0]; 828: xoperands[1] = operands[1]; 829: xoperands[2] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); 830: output_asm_insn ("st %2,[%%fp-4]\n\tst %1,[%%fp-8]\n\tldd [%%fp-8],%0", xoperands); 831: return ""; 832: } 1.1 root 833: } 1.1.1.4 root 834: if (GET_CODE (XEXP (operands[1], 0)) == PLUS 835: && (XEXP (XEXP (operands[1], 0), 0) == frame_pointer_rtx 836: || XEXP (XEXP (operands[1], 0), 0) == stack_pointer_rtx) 837: && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT 838: && (INTVAL (XEXP (XEXP (operands[1], 0), 1)) & 0x7) != 0) 839: { 840: rtx xoperands[2]; 841: output_asm_insn ("ld %1,%0", operands); 842: xoperands[0] = gen_rtx (REG, GET_MODE (operands[0]), 843: REGNO (operands[0]) + 1); 844: xoperands[1] = gen_rtx (MEM, GET_MODE (operands[1]), 845: plus_constant (XEXP (operands[1], 0), 4)); 846: output_asm_insn ("ld %1,%0", xoperands); 847: return ""; 848: } 1.1.1.8 root 849: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) 850: { 851: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) 852: && cc_prev_status.mdep == XEXP (operands[1], 0))) 853: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); 854: cc_status.flags |= CC_KNOW_HI_G1; 855: cc_status.mdep = XEXP (operands[1], 0); 856: return "ldd [%%lo(%m1)+%%g1],%0"; 857: } 1.1 root 858: return "ldd %1,%0"; 859: } 860: else if (FP_REG_P (operands[1])) 861: { 862: if (GET_CODE (operands[0]) == REG) 863: { 1.1.1.3 root 864: if ((REGNO (operands[0]) & 1) == 0) 865: return "std %1,[%%fp-8]\n\tldd [%%fp-8],%0"; 866: else 867: { 868: rtx xoperands[3]; 869: xoperands[2] = operands[1]; 870: xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); 871: xoperands[0] = operands[0]; 872: output_asm_insn ("std %2,[%%fp-8]\n\tld [%%fp-4],%1\n\tld [%%fp-8],%0", xoperands); 873: return ""; 874: } 1.1 root 875: } 1.1.1.4 root 876: if (GET_CODE (XEXP (operands[0], 0)) == PLUS 877: && (XEXP (XEXP (operands[0], 0), 0) == frame_pointer_rtx 878: || XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx) 879: && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT 880: && (INTVAL (XEXP (XEXP (operands[0], 0), 1)) & 0x7) != 0) 881: { 882: rtx xoperands[2]; 1.1.1.8 root 883: output_asm_insn ("st %r1,%0", operands); 1.1.1.4 root 884: xoperands[1] = gen_rtx (REG, GET_MODE (operands[1]), 885: REGNO (operands[1]) + 1); 886: xoperands[0] = gen_rtx (MEM, GET_MODE (operands[0]), 887: plus_constant (XEXP (operands[0], 0), 4)); 1.1.1.8 root 888: output_asm_insn ("st %r1,%0", xoperands); 1.1.1.4 root 889: return ""; 890: } 1.1.1.8 root 891: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) 892: { 893: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) 894: && cc_prev_status.mdep == XEXP (operands[0], 0))) 895: output_asm_insn ("sethi %%hi(%m0),%%g1", operands); 896: cc_status.flags |= CC_KNOW_HI_G1; 897: cc_status.mdep = XEXP (operands[0], 0); 898: return "std %1,[%%lo(%m0)+%%g1]"; 899: } 1.1 root 900: return "std %1,%0"; 901: } 1.1.1.3 root 902: else abort (); 1.1 root 903: } 904: 905: /* Return a REG that occurs in ADDR with coefficient 1. 906: ADDR can be effectively incremented by incrementing REG. */ 907: 908: static rtx 909: find_addr_reg (addr) 910: rtx addr; 911: { 912: while (GET_CODE (addr) == PLUS) 913: { 914: if (GET_CODE (XEXP (addr, 0)) == REG) 915: addr = XEXP (addr, 0); 916: if (GET_CODE (XEXP (addr, 1)) == REG) 917: addr = XEXP (addr, 1); 918: if (CONSTANT_P (XEXP (addr, 0))) 919: addr = XEXP (addr, 1); 920: if (CONSTANT_P (XEXP (addr, 1))) 921: addr = XEXP (addr, 0); 922: } 923: if (GET_CODE (addr) == REG) 924: return addr; 925: return 0; 926: } 927: 1.1.1.8 root 928: void 929: output_sized_memop (opname, mode) 930: char *opname; 931: enum machine_mode mode; 932: { 933: extern struct _iobuf *asm_out_file; 934: 935: static char *ld_size_suffix[] = { "ub", "uh", "", "?", "d" }; 936: static char *st_size_suffix[] = { "b", "h", "", "?", "d" }; 937: char *modename 938: = (opname[0] == 'l' ? ld_size_suffix : st_size_suffix)[GET_MODE_SIZE (mode) >> 1]; 939: 940: fprintf (asm_out_file, "\t%s%s", opname, modename); 941: } 1.1.1.9 ! root 942: ! 943: /* Output a store-in-memory whose operands are OPERANDS[0,1]. ! 944: OPERANDS[0] is a MEM, and OPERANDS[1] is a reg or zero. */ ! 945: ! 946: char * ! 947: output_store (operands) ! 948: rtx *operands; ! 949: { ! 950: enum machine_mode mode = GET_MODE (operands[0]); ! 951: rtx address = XEXP (operands[0], 0); ! 952: ! 953: cc_status.flags |= CC_KNOW_HI_G1; ! 954: cc_status.mdep = address; ! 955: ! 956: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 957: && address == cc_prev_status.mdep)) ! 958: { ! 959: output_asm_insn ("sethi %%hi(%m0),%%g1", operands); ! 960: cc_prev_status.mdep = address; ! 961: } ! 962: ! 963: /* Store zero in two parts when appropriate. */ ! 964: if (mode == DFmode && operands[1] == dconst0_rtx) ! 965: { ! 966: /* We can't cross a page boundary here because the ! 967: SYMBOL_REF must be double word aligned, and for this ! 968: to be the case, SYMBOL_REF+4 cannot cross. */ ! 969: output_sized_memop ("st", SImode); ! 970: output_asm_insn ("%r1,[%%g1+%%lo(%m0)]", operands); ! 971: output_sized_memop ("st", SImode); ! 972: return "%r1,[%%g1+%%lo(%m0)+4]"; ! 973: } ! 974: ! 975: /* Code below isn't smart enough to move a doubleword in two parts, ! 976: so use output_move_double to do that in the cases that require it. */ ! 977: if ((mode == DImode || mode == DFmode) ! 978: && (GET_CODE (operands[1]) == REG ! 979: && (REGNO (operands[1]) & 1))) ! 980: return output_move_double (operands); ! 981: ! 982: output_sized_memop ("st", mode); ! 983: return "%r1,[%%g1+%%lo(%m0)]"; ! 984: } ! 985: ! 986: /* Output a fixed-point load-from-memory whose operands are OPERANDS[0,1]. ! 987: OPERANDS[0] is a reg, and OPERANDS[1] is a mem. */ ! 988: ! 989: char * ! 990: output_load_fixed (operands) ! 991: rtx *operands; ! 992: { ! 993: enum machine_mode mode = GET_MODE (operands[0]); ! 994: rtx address = XEXP (operands[1], 0); ! 995: ! 996: /* We don't bother trying to see if we know %hi(address). ! 997: This is because we are doing a load, and if we know the ! 998: %hi value, we probably also know that value in memory. */ ! 999: cc_status.flags |= CC_KNOW_HI_G1; ! 1000: cc_status.mdep = address; ! 1001: ! 1002: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 1003: && address == cc_prev_status.mdep ! 1004: && cc_prev_status.mdep == cc_status.mdep)) ! 1005: { ! 1006: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); ! 1007: cc_prev_status.mdep = address; ! 1008: } ! 1009: ! 1010: /* Code below isn't smart enough to do a doubleword in two parts. ! 1011: So handle that case the slow way. */ ! 1012: if (mode == DImode ! 1013: && GET_CODE (operands[0]) == REG /* Moving to nonaligned reg pair */ ! 1014: && (REGNO (operands[0]) & 1)) ! 1015: return output_move_double (operands); ! 1016: ! 1017: output_sized_memop ("ld", mode); ! 1018: if (GET_CODE (operands[0]) == REG) ! 1019: return "[%%g1+%%lo(%m1)],%0"; ! 1020: abort (); ! 1021: } ! 1022: ! 1023: /* Output a floating-point load-from-memory whose operands are OPERANDS[0,1]. ! 1024: OPERANDS[0] is a reg, and OPERANDS[1] is a mem. ! 1025: We also handle the case where OPERANDS[0] is a mem. */ ! 1026: ! 1027: char * ! 1028: output_load_floating (operands) ! 1029: rtx *operands; ! 1030: { ! 1031: enum machine_mode mode = GET_MODE (operands[0]); ! 1032: rtx address = XEXP (operands[1], 0); 1.1.1.8 root 1033: 1.1.1.9 ! root 1034: /* We don't bother trying to see if we know %hi(address). ! 1035: This is because we are doing a load, and if we know the ! 1036: %hi value, we probably also know that value in memory. */ ! 1037: cc_status.flags |= CC_KNOW_HI_G1; ! 1038: cc_status.mdep = address; ! 1039: ! 1040: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 1041: && address == cc_prev_status.mdep ! 1042: && cc_prev_status.mdep == cc_status.mdep)) ! 1043: { ! 1044: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); ! 1045: cc_prev_status.mdep = address; ! 1046: } ! 1047: ! 1048: if (mode == DFmode) ! 1049: { ! 1050: if (REG_P (operands[0])) ! 1051: { ! 1052: if (REGNO (operands[0]) & 1) ! 1053: return output_move_double (operands); ! 1054: else ! 1055: return "ldd [%%g1+%%lo(%m1)],%0"; ! 1056: } ! 1057: cc_status.flags &= ~(CC_F0_IS_0|CC_F1_IS_0); ! 1058: output_asm_insn ("ldd [%%g1+%%lo(%m1)],%%f0", operands); ! 1059: operands[1] = gen_rtx (REG, DFmode, 32); ! 1060: return output_fp_move_double (operands); ! 1061: } ! 1062: ! 1063: if (GET_CODE (operands[0]) == MEM) ! 1064: { ! 1065: cc_status.flags &= ~CC_F1_IS_0; ! 1066: output_asm_insn ("ld [%%g1+%%lo(%1)],%%f1", operands); ! 1067: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) ! 1068: { ! 1069: cc_status.mdep = XEXP (operands[0], 0); ! 1070: return "sethi %%hi(%m0),%%g1\n\tst %%f1,[%%g1+%%lo(%m0)]"; ! 1071: } ! 1072: else ! 1073: return "st %%f1,%0"; ! 1074: } ! 1075: return "ld [%%g1+%%lo(%m1)],%0"; ! 1076: } ! 1077: 1.1 root 1078: /* Load the address specified by OPERANDS[3] into the register 1079: specified by OPERANDS[0]. 1080: 1081: OPERANDS[3] may be the result of a sum, hence it could either be: 1082: 1083: (1) CONST 1084: (2) REG 1085: (2) REG + CONST_INT 1086: (3) REG + REG + CONST_INT 1.1.1.8 root 1087: (4) REG + REG (special case of 3). 1.1 root 1088: 1.1.1.5 root 1089: Note that (3) is not a legitimate address. 1.1 root 1090: All cases are handled here. */ 1091: 1092: void 1093: output_load_address (operands) 1094: rtx *operands; 1095: { 1096: rtx base, offset; 1097: 1098: if (CONSTANT_P (operands[3])) 1099: { 1100: output_asm_insn ("set %3,%0", operands); 1101: return; 1102: } 1103: 1104: if (REG_P (operands[3])) 1105: { 1106: if (REGNO (operands[0]) != REGNO (operands[3])) 1107: output_asm_insn ("mov %3,%0", operands); 1108: return; 1109: } 1110: 1.1.1.8 root 1111: if (GET_CODE (operands[3]) != PLUS) 1112: abort (); 1113: 1.1 root 1114: base = XEXP (operands[3], 0); 1115: offset = XEXP (operands[3], 1); 1116: 1117: if (GET_CODE (base) == CONST_INT) 1118: { 1119: rtx tmp = base; 1120: base = offset; 1121: offset = tmp; 1122: } 1123: 1124: if (GET_CODE (offset) != CONST_INT) 1.1.1.8 root 1125: { 1126: /* Operand is (PLUS (REG) (REG)). */ 1127: base = operands[3]; 1128: offset = const0_rtx; 1129: } 1.1 root 1130: 1131: if (REG_P (base)) 1132: { 1133: operands[6] = base; 1134: operands[7] = offset; 1135: if (SMALL_INT (offset)) 1136: output_asm_insn ("add %6,%7,%0", operands); 1137: else 1138: output_asm_insn ("set %7,%0\n\tadd %0,%6,%0", operands); 1139: } 1.1.1.8 root 1140: else if (GET_CODE (base) == PLUS) 1.1 root 1141: { 1142: operands[6] = XEXP (base, 0); 1143: operands[7] = XEXP (base, 1); 1144: operands[8] = offset; 1145: 1146: if (SMALL_INT (offset)) 1147: output_asm_insn ("add %6,%7,%0\n\tadd %0,%8,%0", operands); 1148: else 1149: output_asm_insn ("set %8,%0\n\tadd %0,%6,%0\n\tadd %0,%7,%0", operands); 1150: } 1.1.1.8 root 1151: else 1152: abort (); 1.1 root 1153: } 1154: 1.1.1.5 root 1155: /* Output code to place a size count SIZE in register REG. 1156: If SIZE is round, then assume that we can use alignment 1157: based on that roundness, and return an integer saying 1158: what alignment (roundness, transfer size) we will be using. 1159: 1160: Because block moves are pipelined, we don't include the 1161: first element in the transfer of SIZE to REG. */ 1162: 1163: static int 1164: output_size_for_block_move (size, reg) 1165: rtx size, reg; 1166: { 1167: int align; 1168: rtx xoperands[2]; 1169: xoperands[0] = reg; 1170: 1171: /* First, figure out best alignment we may assume. */ 1172: if (REG_P (size)) 1173: { 1174: xoperands[1] = size; 1175: output_asm_insn ("sub %1,1,%0", xoperands); 1176: align = 1; 1177: } 1178: else 1179: { 1180: int i = INTVAL (size); 1181: 1182: if (i & 1) 1183: align = 1; 1184: else if (i & 3) 1185: align = 2; 1186: else 1187: align = 4; 1188: 1189: /* predecrement count. */ 1190: i -= align; 1191: if (i < 0) abort (); 1192: 1193: xoperands[1] = gen_rtx (CONST_INT, VOIDmode, i); 1194: 1195: output_asm_insn ("set %1,%0", xoperands); 1196: } 1197: return align; 1198: } 1199: 1200: /* Emit code to perform a block move. 1201: 1202: OPERANDS[0] is the destination. 1203: OPERANDS[1] is the source. 1204: OPERANDS[2] is the size. 1.1.1.8 root 1205: OPERANDS[3..5] are pseudos we can safely clobber as temps. */ 1.1.1.5 root 1206: 1.1 root 1207: char * 1208: output_block_move (operands) 1209: rtx *operands; 1210: { 1.1.1.5 root 1211: /* A vector for our computed operands. Note that load_output_address 1.1.1.8 root 1212: makes use of (and can clobber) up to the 8th element of this vector. */ 1.1.1.5 root 1213: rtx xoperands[10]; 1.1.1.8 root 1214: rtx zoperands[10]; 1.1 root 1215: static int movstrsi_label = 0; 1.1.1.5 root 1216: int align = -1; /* not yet known */ 1.1 root 1217: int i, j; 1218: 1.1.1.8 root 1219: xoperands[0] = operands[0]; 1220: xoperands[1] = operands[1]; 1221: xoperands[2] = operands[3]; 1.1.1.5 root 1222: 1.1 root 1223: /* Since we clobber untold things, nix the condition codes. */ 1224: CC_STATUS_INIT; 1225: 1.1.1.5 root 1226: /* Recognize special cases of block moves. These occur 1227: when GNU C++ is forced to treat something as BLKmode 1228: to keep it in memory, when its mode could be represented 1.1.1.8 root 1229: with something smaller. 1230: 1231: We cannot do this for global variables, since we don't know 1232: what pages they don't cross. Sigh. */ 1.1.1.5 root 1233: if (GET_CODE (operands[2]) == CONST_INT 1.1.1.8 root 1234: && INTVAL (operands[2]) <= 16 1235: && ! CONSTANT_ADDRESS_P (operands[0]) 1236: && ! CONSTANT_ADDRESS_P (operands[1])) 1.1.1.5 root 1237: { 1238: int size = INTVAL (operands[2]); 1239: 1.1.1.8 root 1240: cc_status.flags &= ~CC_KNOW_HI_G1; 1.1.1.5 root 1241: if (size & 1) 1242: { 1243: if (memory_address_p (QImode, plus_constant (xoperands[0], size)) 1244: && memory_address_p (QImode, plus_constant (xoperands[1], size))) 1245: { 1.1.1.8 root 1246: /* We will store different integers into this particular RTX. */ 1247: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 1248: for (i = size-1; i >= 0; i--) 1249: { 1250: INTVAL (xoperands[2]) = i; 1251: output_asm_insn ("ldub [%a1+%2],%%g1\n\tstb %%g1,[%a0+%2]", 1252: xoperands); 1253: } 1254: return ""; 1255: } 1256: } 1257: else if (size & 2) 1258: { 1259: if (memory_address_p (HImode, plus_constant (xoperands[0], size)) 1260: && memory_address_p (HImode, plus_constant (xoperands[1], size))) 1261: { 1.1.1.8 root 1262: /* We will store different integers into this particular RTX. */ 1263: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 1264: for (i = (size>>1)-1; i >= 0; i--) 1265: { 1266: INTVAL (xoperands[2]) = i<<1; 1267: output_asm_insn ("lduh [%a1+%2],%%g1\n\tsth %%g1,[%a0+%2]", 1268: xoperands); 1269: } 1270: return ""; 1271: } 1272: } 1273: else 1274: { 1275: if (memory_address_p (SImode, plus_constant (xoperands[0], size)) 1276: && memory_address_p (SImode, plus_constant (xoperands[1], size))) 1277: { 1.1.1.8 root 1278: /* We will store different integers into this particular RTX. */ 1279: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 1280: for (i = (size>>2)-1; i >= 0; i--) 1281: { 1282: INTVAL (xoperands[2]) = i<<2; 1283: output_asm_insn ("ld [%a1+%2],%%g1\n\tst %%g1,[%a0+%2]", 1284: xoperands); 1285: } 1286: return ""; 1287: } 1288: } 1289: } 1290: 1.1.1.8 root 1291: /* This is the size of the transfer. 1292: Either use the register which already contains the size, 1293: or use a free register (used by no operands). 1294: Also emit code to decrement the size value by ALIGN. */ 1295: align = output_size_for_block_move (operands[2], operands[3]); 1.1.1.5 root 1296: 1.1.1.8 root 1297: zoperands[0] = operands[0]; 1298: zoperands[3] = plus_constant (operands[0], align); 1299: output_load_address (zoperands); 1.1 root 1300: 1301: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); 1302: xoperands[4] = gen_rtx (CONST_INT, VOIDmode, align); 1303: 1.1.1.5 root 1304: if (align == 1) 1305: output_asm_insn ("\nLm%3:\n\tldub [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tstb %%g1,[%0+%2]", xoperands); 1306: else if (align == 2) 1307: output_asm_insn ("\nLm%3:\n\tlduh [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tsth %%g1,[%0+%2]", xoperands); 1308: else 1309: output_asm_insn ("\nLm%3:\n\tld [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tst %%g1,[%0+%2]", xoperands); 1.1 root 1310: return ""; 1311: } 1.1.1.5 root 1312: 1313: /* What the sparc lacks in hardware, make up for in software. 1314: Compute a fairly good sequence of shift and add insns 1315: to make a multiply happen. */ 1.1 root 1316: 1317: #define ABS(x) ((x) < 0 ? -(x) : x) 1318: 1319: char * 1320: output_mul_by_constant (insn, operands, unsignedp) 1321: rtx insn; 1322: rtx *operands; 1323: int unsignedp; 1324: { 1325: int c; /* Size of constant */ 1326: int shifts[BITS_PER_WORD]; /* Table of shifts */ 1.1.1.4 root 1327: unsigned int p, log; /* A power of two, and its log */ 1.1 root 1328: int d1, d2; /* Differences of c and p */ 1329: int first = 1; /* True if dst has unknown data in it */ 1330: int i; 1331: 1332: c = INTVAL (operands[2]); 1333: if (c == 0) 1334: { 1335: /* should not happen. */ 1336: abort (); 1337: if (GET_CODE (operands[0]) == MEM) 1338: return "st %%g0,%0"; 1339: return "mov %%g0,%0"; 1340: } 1341: 1342: output_asm_insn ("! start open coded multiply"); 1343: 1344: /* Clear out the table of shifts. */ 1345: for (i = 0; i < BITS_PER_WORD; ++i) 1346: shifts[i] = 0; 1347: 1348: while (c) 1349: { 1350: /* Find the power of two nearest ABS(c) */ 1351: p = 1, log = 0; 1352: do 1353: { 1354: d1 = ABS(c) - p; 1355: p *= 2; 1356: ++log; 1357: } 1358: while (p < ABS(c)); 1359: d2 = p - ABS(c); 1360: 1361: /* Make an appropriate entry in shifts for p. */ 1362: if (d2 < d1) 1363: { 1364: shifts[log] = c < 0 ? -1 : 1; 1365: c = c < 0 ? d2 : -d2; 1366: } 1367: else 1368: { 1369: shifts[log - 1] = c < 0 ? -1 : 1; 1370: c = c < 0 ? -d1 : d1; 1371: } 1372: } 1373: 1374: /* Take care of the first insn in sequence. 1375: We know we have at least one. */ 1376: 1377: /* A value of -1 in shifts says to subtract that power of two, and a value 1378: of 1 says to add that power of two. */ 1379: for (i = 0; ; i++) 1380: if (shifts[i]) 1381: { 1382: if (i) 1383: { 1384: operands[2] = gen_rtx (CONST_INT, VOIDmode, i); 1.1.1.5 root 1385: output_asm_insn ("sll %1,%2,%%g1", operands); 1.1 root 1386: } 1.1.1.5 root 1387: else output_asm_insn ("mov %1,%%g1", operands); 1.1 root 1388: 1389: log = i; 1390: if (shifts[i] < 0) 1.1.1.5 root 1391: output_asm_insn ("sub %%g0,%%g1,%0", operands); 1.1 root 1392: else 1.1.1.5 root 1393: output_asm_insn ("mov %%g1,%0", operands); 1.1 root 1394: break; 1395: } 1396: 1397: /* A value of -1 in shifts says to subtract that power of two, and a value 1398: of 1 says to add that power of two--continued. */ 1399: for (i += 1; i < BITS_PER_WORD; ++i) 1400: if (shifts[i]) 1401: { 1402: if (i - log > 0) 1403: { 1404: operands[2] = gen_rtx (CONST_INT, VOIDmode, i - log); 1.1.1.5 root 1405: output_asm_insn ("sll %%g1,%2,%%g1", operands); 1.1 root 1406: } 1407: else 1408: { 1409: operands[2] = gen_rtx (CONST_INT, VOIDmode, log - i); 1.1.1.5 root 1410: output_asm_insn ("sra %%g1,%2,%%g1", operands); 1.1 root 1411: } 1412: log = i; 1413: if (shifts[i] < 0) 1.1.1.5 root 1414: output_asm_insn ("sub %0,%%g1,%0", operands); 1.1 root 1415: else 1.1.1.5 root 1416: output_asm_insn ("add %0,%%g1,%0", operands); 1.1 root 1417: } 1418: 1419: output_asm_insn ("! end open coded multiply"); 1420: 1421: return ""; 1422: } 1423: 1424: char * 1425: output_mul_insn (operands, unsignedp) 1426: rtx *operands; 1427: int unsignedp; 1428: { 1429: int lucky1 = ((unsigned)REGNO (operands[1]) - 8) <= 1; 1430: int lucky2 = ((unsigned)REGNO (operands[2]) - 8) <= 1; 1431: 1432: if (lucky1) 1433: if (lucky2) 1434: output_asm_insn ("call .mul,2\n\tnop", operands); 1435: else 1436: { 1437: rtx xoperands[2]; 1438: xoperands[0] = gen_rtx (REG, SImode, 1439: 8 ^ (REGNO (operands[1]) == 8)); 1440: xoperands[1] = operands[2]; 1441: output_asm_insn ("call .mul,2\n\tmov %1,%0", xoperands); 1442: } 1443: else if (lucky2) 1444: { 1445: rtx xoperands[2]; 1446: xoperands[0] = gen_rtx (REG, SImode, 1447: 8 ^ (REGNO (operands[2]) == 8)); 1448: xoperands[1] = operands[1]; 1449: output_asm_insn ("call .mul,2\n\tmov %1,%0", xoperands); 1450: } 1451: else 1452: { 1453: output_asm_insn ("mov %1,%%o0\n\tcall .mul,2\n\tmov %2,%%o1", 1454: operands); 1455: } 1456: 1457: if (REGNO (operands[0]) == 8) 1458: return ""; 1459: return "mov %%o0,%0"; 1460: } 1461: 1462: /* Make floating point register f0 contain 0. 1463: SIZE is the number of registers (including f0) 1464: which should contain 0. */ 1465: 1466: void 1467: make_f0_contain_0 (size) 1468: int size; 1469: { 1470: if (size == 1) 1.1.1.8 root 1471: { 1472: if ((cc_status.flags & (CC_F0_IS_0)) == 0) 1473: output_asm_insn ("ld [%%fp-16],%%f0", 0); 1474: cc_status.flags |= CC_F0_IS_0; 1475: } 1.1 root 1476: else if (size == 2) 1.1.1.8 root 1477: { 1478: if ((cc_status.flags & CC_F0_IS_0) == 0) 1479: output_asm_insn ("ld [%%fp-16],%%f0", 0); 1480: if ((cc_status.flags & (CC_F1_IS_0)) == 0) 1481: output_asm_insn ("ld [%%fp-12],%%f1", 0); 1482: cc_status.flags |= CC_F0_IS_0 | CC_F1_IS_0; 1483: } 1484: } 1485: 1486: /* Since condition codes don't have logical links, we need to keep 1487: their setting and use together for set-cc insns. */ 1488: void 1489: gen_scc_insn (code, mode, operands) 1490: enum rtx_code code; 1491: enum machine_mode mode; 1492: rtx *operands; 1493: { 1494: extern rtx sequence_stack; 1495: rtx last_insn = XEXP (XEXP (sequence_stack, 1), 0); 1496: rtx last_pat = PATTERN (last_insn); 1497: if (GET_CODE (last_pat) != SET 1498: || GET_CODE (SET_DEST (last_pat)) != CC0) 1499: abort (); 1500: SET_DEST (last_pat) = operands[0]; 1501: SET_SRC (last_pat) = gen_rtx (code, mode, SET_SRC (last_pat), const0_rtx); 1.1 root 1502: } 1.1.1.4 root 1503: 1504: /* Output reasonable peephole for set-on-condition-code insns. 1505: Note that these insns assume a particular way of defining 1506: labels. Therefore, *both* tm-sparc.h and this function must 1507: be changed if a new syntax is needed. */ 1508: 1509: char * 1.1.1.8 root 1510: output_scc_insn (code, operand) 1.1.1.4 root 1511: enum rtx_code code; 1.1.1.8 root 1512: rtx operand; 1.1.1.4 root 1513: { 1514: rtx xoperands[2]; 1515: rtx label = gen_label_rtx (); 1.1.1.8 root 1516: int cc_in_fccr = cc_status.flags & CC_IN_FCCR; 1.1.1.4 root 1517: 1.1.1.8 root 1518: xoperands[0] = operand; 1.1.1.4 root 1519: xoperands[1] = label; 1520: 1521: switch (code) 1522: { 1523: case NE: 1.1.1.8 root 1524: if (cc_in_fccr) 1.1.1.5 root 1525: output_asm_insn ("fbne,a %l0", &label); 1.1.1.4 root 1526: else 1.1.1.5 root 1527: output_asm_insn ("bne,a %l0", &label); 1.1.1.4 root 1528: break; 1529: case EQ: 1.1.1.8 root 1530: if (cc_in_fccr) 1.1.1.5 root 1531: output_asm_insn ("fbe,a %l0", &label); 1.1.1.4 root 1532: else 1.1.1.5 root 1533: output_asm_insn ("be,a %l0", &label); 1.1.1.4 root 1534: break; 1535: case GE: 1.1.1.8 root 1536: if (cc_in_fccr) 1.1.1.5 root 1537: output_asm_insn ("fbge,a %l0", &label); 1.1.1.4 root 1538: else 1.1.1.5 root 1539: output_asm_insn ("bge,a %l0", &label); 1.1.1.4 root 1540: break; 1541: case GT: 1.1.1.8 root 1542: if (cc_in_fccr) 1.1.1.5 root 1543: output_asm_insn ("fbg,a %l0", &label); 1.1.1.4 root 1544: else 1.1.1.5 root 1545: output_asm_insn ("bg,a %l0", &label); 1.1.1.4 root 1546: break; 1547: case LE: 1.1.1.8 root 1548: if (cc_in_fccr) 1.1.1.5 root 1549: output_asm_insn ("fble,a %l0", &label); 1.1.1.4 root 1550: else 1.1.1.5 root 1551: output_asm_insn ("ble,a %l0", &label); 1.1.1.4 root 1552: break; 1553: case LT: 1.1.1.8 root 1554: if (cc_in_fccr) 1.1.1.5 root 1555: output_asm_insn ("fbl,a %l0", &label); 1.1.1.4 root 1556: else 1.1.1.5 root 1557: output_asm_insn ("bl,a %l0", &label); 1.1.1.4 root 1558: break; 1559: case GEU: 1.1.1.8 root 1560: if (cc_in_fccr) 1.1.1.4 root 1561: abort (); 1562: else 1.1.1.5 root 1563: output_asm_insn ("bgeu,a %l0", &label); 1.1.1.4 root 1564: break; 1565: case GTU: 1.1.1.8 root 1566: if (cc_in_fccr) 1.1.1.4 root 1567: abort (); 1568: else 1.1.1.5 root 1569: output_asm_insn ("bgu,a %l0", &label); 1.1.1.4 root 1570: break; 1571: case LEU: 1.1.1.8 root 1572: if (cc_in_fccr) 1.1.1.4 root 1573: abort (); 1574: else 1.1.1.5 root 1575: output_asm_insn ("bleu,a %l0", &label); 1.1.1.4 root 1576: break; 1577: case LTU: 1.1.1.8 root 1578: if (cc_in_fccr) 1.1.1.4 root 1579: abort (); 1580: else 1.1.1.5 root 1581: output_asm_insn ("blu,a %l0", &label); 1.1.1.4 root 1582: break; 1583: default: 1584: abort (); 1585: } 1.1.1.8 root 1586: output_asm_insn ("mov 1,%0\n\tmov 0,%0\n%l1:", xoperands); 1.1.1.4 root 1587: return ""; 1588: } 1589: 1.1.1.5 root 1590: /* Output a delayed branch insn with the delay insn in its 1591: branch slot. The delayed branch insn template is in TEMPLATE, 1.1.1.8 root 1592: with operands OPERANDS. The insn in its delay slot is INSN. 1593: 1594: As a special case, since we know that all memory transfers are via 1595: ld/st insns, if we see a (MEM (SYMBOL_REF ...)) we divide the memory 1596: reference around the branch as 1597: 1598: sethi %hi(x),%%g1 1599: b ... 1600: ld/st [%g1+%lo(x)],... 1601: 1.1.1.9 ! root 1602: As another special case, we handle loading (SYMBOL_REF ...) and ! 1603: other large constants around branches as well: ! 1604: ! 1605: sethi %hi(x),%0 ! 1606: b ... ! 1607: or %0,%lo(x),%1 ! 1608: 1.1.1.8 root 1609: */ 1.1.1.4 root 1610: 1611: char * 1.1.1.9 ! root 1612: output_delayed_branch (template, operands, insn) 1.1.1.4 root 1613: char *template; 1614: rtx *operands; 1615: rtx insn; 1616: { 1.1.1.9 ! root 1617: extern rtx recog_operand[]; 1.1.1.8 root 1618: rtx src = XVECEXP (PATTERN (insn), 0, 1); 1619: rtx dest = XVECEXP (PATTERN (insn), 0, 0); 1620: 1.1.1.9 ! root 1621: if (GET_CODE (src) == SYMBOL_REF ! 1622: || (GET_CODE (src) == CONST_INT ! 1623: && !(SMALL_INT (src) || (INTVAL (src) & 0x3ff) == 0))) ! 1624: { ! 1625: rtx xoperands[2]; ! 1626: xoperands[0] = dest; ! 1627: xoperands[1] = src; ! 1628: ! 1629: /* Output the `sethi' insn. */ ! 1630: output_asm_insn ("sethi %%hi(%1),%0", xoperands); ! 1631: ! 1632: /* Output the branch instruction next. */ ! 1633: output_asm_insn (template, operands); ! 1634: ! 1635: /* Now output the `or' insn. */ ! 1636: output_asm_insn ("or %0,%%lo(%1),%0", xoperands); ! 1637: } ! 1638: else if ((GET_CODE (src) == MEM ! 1639: && CONSTANT_ADDRESS_P (XEXP (src, 0))) ! 1640: || (GET_CODE (dest) == MEM ! 1641: && CONSTANT_ADDRESS_P (XEXP (dest, 0)))) 1.1.1.8 root 1642: { 1643: rtx xoperands[2]; 1644: char *split_template; 1645: xoperands[0] = dest; 1646: xoperands[1] = src; 1647: 1648: /* Output the `sethi' insn. */ 1649: if (GET_CODE (src) == MEM) 1650: { 1.1.1.9 ! root 1651: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 1652: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 1653: output_asm_insn ("sethi %%hi(%m1),%%g1", xoperands); 1.1.1.8 root 1654: split_template = "ld [%%g1+%%lo(%m1)],%0"; 1655: } 1656: else 1657: { 1.1.1.9 ! root 1658: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 1659: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 1660: output_asm_insn ("sethi %%hi(%m0),%%g1", xoperands); 1.1.1.8 root 1661: split_template = "st %r1,[%%g1+%%lo(%m0)]"; 1662: } 1.1.1.4 root 1663: 1.1.1.8 root 1664: /* Output the branch instruction next. */ 1665: output_asm_insn (template, operands); 1666: 1667: /* Now output the load or store. 1668: No need to do a CC_STATUS_INIT, because we are branching anyway. */ 1669: output_asm_insn (split_template, xoperands); 1670: } 1671: else 1672: { 1673: extern char *insn_template[]; 1674: extern char *(*insn_outfun[])(); 1675: int insn_code_number; 1676: rtx pat = gen_rtx (SET, VOIDmode, dest, src); 1677: rtx delay_insn = gen_rtx (INSN, VOIDmode, 0, 0, 0, pat, -1, 0, 0); 1678: 1679: /* Output the branch instruction first. */ 1680: output_asm_insn (template, operands); 1681: 1682: /* Now recognize the insn which we put in its delay slot. 1683: We must do this after outputing the branch insn, 1.1.1.9 ! root 1684: since operands may just be a pointer to `recog_operand'. */ 1.1.1.8 root 1685: insn_code_number = recog (pat, delay_insn); 1686: if (insn_code_number == -1) 1687: abort (); 1688: 1689: /* Now get the template for what this insn would 1690: have been, without the branch. Its operands are 1691: exactly the same as they would be, so we don't 1692: need to do an insn_extract. */ 1693: template = insn_template[insn_code_number]; 1694: if (template == 0) 1.1.1.9 ! root 1695: template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); ! 1696: output_asm_insn (template, recog_operand); 1.1.1.8 root 1697: } 1.1.1.9 ! root 1698: CC_STATUS_INIT; 1.1.1.4 root 1699: return ""; 1700: } 1.1.1.9 ! root 1701: ! 1702: /* Output a newly constructed insn DELAY_INSN. */ ! 1703: char * ! 1704: output_delay_insn (delay_insn) ! 1705: rtx delay_insn; ! 1706: { ! 1707: char *template; ! 1708: extern rtx recog_operand[]; ! 1709: extern char call_used_regs[]; ! 1710: extern char *insn_template[]; ! 1711: extern int insn_n_operands[]; ! 1712: extern char *(*insn_outfun[])(); ! 1713: extern rtx alter_subreg(); ! 1714: int insn_code_number; ! 1715: extern int insn_n_operands[]; ! 1716: int i; ! 1717: ! 1718: /* Now recognize the insn which we put in its delay slot. ! 1719: We must do this after outputing the branch insn, ! 1720: since operands may just be a pointer to `recog_operand'. */ ! 1721: insn_code_number = recog_memoized (delay_insn); ! 1722: if (insn_code_number == -1) ! 1723: abort (); ! 1724: ! 1725: /* Extract the operands of this delay insn. */ ! 1726: INSN_CODE (delay_insn) = insn_code_number; ! 1727: insn_extract (delay_insn); ! 1728: ! 1729: /* It is possible that this insn has not been properly scaned by final ! 1730: yet. If this insn's operands don't appear in the peephole's ! 1731: actual operands, then they won't be fixed up by final, so we ! 1732: make sure they get fixed up here. -- This is a kludge. */ ! 1733: for (i = 0; i < insn_n_operands[insn_code_number]; i++) ! 1734: { ! 1735: if (GET_CODE (recog_operand[i]) == SUBREG) ! 1736: recog_operand[i] = alter_subreg (recog_operand[i]); ! 1737: } ! 1738: ! 1739: #ifdef REGISTER_CONSTRAINTS ! 1740: if (! constrain_operands (insn_code_number)) ! 1741: abort (); ! 1742: #endif ! 1743: ! 1744: cc_prev_status = cc_status; ! 1745: ! 1746: /* Update `cc_status' for this instruction. ! 1747: The instruction's output routine may change it further. ! 1748: If the output routine for a jump insn needs to depend ! 1749: on the cc status, it should look at cc_prev_status. */ ! 1750: ! 1751: NOTICE_UPDATE_CC (PATTERN (delay_insn), delay_insn); ! 1752: ! 1753: /* Now get the template for what this insn would ! 1754: have been, without the branch. */ ! 1755: ! 1756: template = insn_template[insn_code_number]; ! 1757: if (template == 0) ! 1758: template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); ! 1759: output_asm_insn (template, recog_operand); ! 1760: return ""; ! 1761: } ! 1762: ! 1763: /* Output the insn HEAD, keeping OPERANDS protected (wherever they are). ! 1764: HEAD comes from the target of some branch, so before we output it, ! 1765: we delete it from the target, lest we execute it twice. The caller ! 1766: of this function promises that such code motion is permissable. */ ! 1767: char * ! 1768: output_eager_then_insn (head, operands) ! 1769: rtx head; ! 1770: rtx *operands; ! 1771: { ! 1772: extern rtx alter_subreg (); ! 1773: extern int insn_n_operands[]; ! 1774: extern rtx recog_operand[]; ! 1775: rtx xoperands[MAX_RECOG_OPERANDS]; ! 1776: int insn_code_number, i, nbytes; ! 1777: rtx nhead; ! 1778: ! 1779: /* Micro-hack: run peephole on head if it looks like a good idea. ! 1780: Right now there's only one such case worth doing... ! 1781: ! 1782: This could be made smarter if the peephole for ``2-insn combine'' ! 1783: were also made smarter. */ ! 1784: if (GET_CODE (PATTERN (head)) == SET ! 1785: && REG_P (SET_SRC (PATTERN (head))) ! 1786: && REG_P (SET_DEST (PATTERN (head))) ! 1787: && (nhead = next_real_insn_no_labels (head)) ! 1788: && GET_CODE (nhead) == INSN ! 1789: && GET_CODE (PATTERN (nhead)) == SET ! 1790: && GET_CODE (SET_DEST (PATTERN (nhead))) == CC0 ! 1791: && (SET_SRC (PATTERN (nhead)) == SET_SRC (PATTERN (head)) ! 1792: || SET_SRC (PATTERN (nhead)) == SET_DEST (PATTERN (head)))) ! 1793: /* Something's wrong if this does not fly. */ ! 1794: if (! peephole (head)) ! 1795: abort (); ! 1796: ! 1797: /* Save our contents of `operands', since output_delay_insn sets them. */ ! 1798: insn_code_number = recog_memoized (head); ! 1799: nbytes = insn_n_operands[insn_code_number] * sizeof (rtx); ! 1800: bcopy (operands, xoperands, nbytes); ! 1801: ! 1802: /* Output the delay insn, and prevent duplication later. */ ! 1803: delete_insn (head); ! 1804: output_delay_insn (head); ! 1805: ! 1806: /* Restore this insn's operands. */ ! 1807: bcopy (xoperands, operands, nbytes); ! 1808: } ! 1809: ! 1810: /* Return the next INSN, CALL_INSN or JUMP_INSN after LABEL; ! 1811: or 0, if there is none. Also return 0 if we cross a label. */ ! 1812: ! 1813: rtx ! 1814: next_real_insn_no_labels (label) ! 1815: rtx label; ! 1816: { ! 1817: register rtx insn = NEXT_INSN (label); ! 1818: register RTX_CODE code; ! 1819: ! 1820: while (insn) ! 1821: { ! 1822: code = GET_CODE (insn); ! 1823: if (code == INSN) ! 1824: { ! 1825: if (GET_CODE (PATTERN (insn)) != CLOBBER ! 1826: && GET_CODE (PATTERN (insn)) != USE) ! 1827: return insn; ! 1828: } ! 1829: if (code == CALL_INSN || code == JUMP_INSN) ! 1830: return insn; ! 1831: if (code == CODE_LABEL) ! 1832: return 0; ! 1833: insn = NEXT_INSN (insn); ! 1834: } ! 1835: ! 1836: return 0; ! 1837: } ! 1838: ! 1839: int ! 1840: operands_satisfy_eager_branch_peephole (operands, conditional) ! 1841: rtx *operands; ! 1842: int conditional; ! 1843: { ! 1844: rtx label; ! 1845: ! 1846: if (conditional) ! 1847: { ! 1848: if (GET_CODE (operands[0]) != IF_THEN_ELSE) ! 1849: return 0; ! 1850: ! 1851: if (GET_CODE (XEXP (operands[0], 1)) == LABEL_REF) ! 1852: label = XEXP (XEXP (operands[0], 1), 0); ! 1853: else if (GET_CODE (XEXP (operands[0], 2)) == LABEL_REF) ! 1854: label = XEXP (XEXP (operands[0], 2), 0); ! 1855: else return 0; ! 1856: } ! 1857: else ! 1858: { ! 1859: label = operands[0]; ! 1860: } ! 1861: ! 1862: if (LABEL_NUSES (label) == 1) ! 1863: { ! 1864: rtx prev = PREV_INSN (label); ! 1865: while (prev && GET_CODE (prev) == NOTE) ! 1866: prev = PREV_INSN (prev); ! 1867: if (prev == 0 ! 1868: || GET_CODE (prev) == BARRIER) ! 1869: { ! 1870: rtx head = next_real_insn_no_labels (label); ! 1871: ! 1872: if (head ! 1873: && ! INSN_DELETED_P (head) ! 1874: && GET_CODE (head) == INSN ! 1875: && GET_CODE (PATTERN (head)) == SET ! 1876: && strict_single_insn_op_p (SET_SRC (PATTERN (head)), ! 1877: GET_MODE (SET_DEST (PATTERN (head)))) ! 1878: && strict_single_insn_op_p (SET_DEST (PATTERN (head)), ! 1879: GET_MODE (SET_DEST (PATTERN (head))))) ! 1880: { ! 1881: if (conditional == 2) ! 1882: return (GET_CODE (operands[1]) != PC ! 1883: && safe_insn_src_p (operands[2], VOIDmode) ! 1884: && strict_single_insn_op_p (operands[2], VOIDmode) ! 1885: && operand_clobbered_before_used_after (operands[1], label)); ! 1886: return 1; ! 1887: } ! 1888: } ! 1889: } ! 1890: ! 1891: if (conditional == 1 ! 1892: && GET_CODE (operands[1]) != PC ! 1893: && safe_insn_src_p (operands[2], VOIDmode) ! 1894: && strict_single_insn_op_p (operands[2], VOIDmode) ! 1895: && operand_clobbered_before_used_after (operands[1], label)) ! 1896: return 1; ! 1897: ! 1898: return 0; ! 1899: } ! 1900:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.