|
|
1.1 ! root 1: /* Subroutines for insn-output.c for HPPA. ! 2: Copyright (C) 1992, 1993 Free Software Foundation, Inc. ! 3: Contributed by Tim Moore ([email protected]), based on 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 2, 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: #include <stdio.h> ! 22: #include "config.h" ! 23: #include "rtl.h" ! 24: #include "regs.h" ! 25: #include "hard-reg-set.h" ! 26: #include "real.h" ! 27: #include "insn-config.h" ! 28: #include "conditions.h" ! 29: #include "insn-flags.h" ! 30: #include "output.h" ! 31: #include "insn-attr.h" ! 32: #include "flags.h" ! 33: #include "tree.h" ! 34: #include "c-tree.h" ! 35: #include "expr.h" ! 36: #include "obstack.h" ! 37: #include "machopic.h" ! 38: /* Save the operands last given to a compare for use when we ! 39: generate a scc or bcc insn. */ ! 40: ! 41: rtx hppa_compare_op0, hppa_compare_op1; ! 42: enum cmp_type hppa_branch_type; ! 43: ! 44: rtx hppa_save_pic_table_rtx; ! 45: ! 46: /* Set by the FUNCTION_PROFILER macro. */ ! 47: int hp_profile_labelno; ! 48: extern int profile_label_no; ! 49: ! 50: /* Counts for the number of callee-saved general and floating point ! 51: registers which were saved by the current function's prologue. */ ! 52: static int gr_saved, fr_saved; ! 53: ! 54: static rtx find_addr_reg (); ! 55: ! 56: /* Return non-zero only if OP is a register of mode MODE, ! 57: or CONST0_RTX. */ ! 58: int ! 59: reg_or_0_operand (op, mode) ! 60: rtx op; ! 61: enum machine_mode mode; ! 62: { ! 63: return (op == CONST0_RTX (mode) || register_operand (op, mode)); ! 64: } ! 65: ! 66: /* Return non-zero if OP is suitable for use in a call to a named ! 67: function. ! 68: ! 69: (???) For 2.5 try to eliminate either call_operand_address or ! 70: function_label_operand, they perform very similar functions. */ ! 71: int ! 72: call_operand_address (op, mode) ! 73: rtx op; ! 74: enum machine_mode mode; ! 75: { ! 76: return (CONSTANT_P (op) && ! TARGET_LONG_CALLS); ! 77: } ! 78: ! 79: /* Return 1 if X contains a symbolic expression. We know these ! 80: expressions will have one of a few well defined forms, so ! 81: we need only check those forms. */ ! 82: int ! 83: symbolic_expression_p (x) ! 84: register rtx x; ! 85: { ! 86: ! 87: /* Strip off any HIGH. */ ! 88: if (GET_CODE (x) == HIGH) ! 89: x = XEXP (x, 0); ! 90: ! 91: return (symbolic_operand (x, VOIDmode)); ! 92: } ! 93: ! 94: int ! 95: symbolic_operand (op, mode) ! 96: register rtx op; ! 97: enum machine_mode mode; ! 98: { ! 99: switch (GET_CODE (op)) ! 100: { ! 101: case SYMBOL_REF: ! 102: case LABEL_REF: ! 103: return 1; ! 104: case CONST: ! 105: op = XEXP (op, 0); ! 106: return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF ! 107: || GET_CODE (XEXP (op, 0)) == LABEL_REF) ! 108: && GET_CODE (XEXP (op, 1)) == CONST_INT); ! 109: default: ! 110: return 0; ! 111: } ! 112: } ! 113: ! 114: /* Return truth value of statement that OP is a symbolic memory ! 115: operand of mode MODE. */ ! 116: ! 117: int ! 118: symbolic_memory_operand (op, mode) ! 119: rtx op; ! 120: enum machine_mode mode; ! 121: { ! 122: if (GET_CODE (op) == SUBREG) ! 123: op = SUBREG_REG (op); ! 124: if (GET_CODE (op) != MEM) ! 125: return 0; ! 126: op = XEXP (op, 0); ! 127: return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST ! 128: || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); ! 129: } ! 130: ! 131: /* Return 1 if the operand is either a register or a memory operand that is ! 132: not symbolic. */ ! 133: ! 134: int ! 135: reg_or_nonsymb_mem_operand (op, mode) ! 136: register rtx op; ! 137: enum machine_mode mode; ! 138: { ! 139: if (register_operand (op, mode)) ! 140: return 1; ! 141: ! 142: if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) ! 143: return 1; ! 144: ! 145: return 0; ! 146: } ! 147: ! 148: /* Return 1 if the operand is either a register, zero, or a memory operand ! 149: that is not symbolic. */ ! 150: ! 151: int ! 152: reg_or_0_or_nonsymb_mem_operand (op, mode) ! 153: register rtx op; ! 154: enum machine_mode mode; ! 155: { ! 156: if (register_operand (op, mode)) ! 157: return 1; ! 158: ! 159: if (op == CONST0_RTX (mode)) ! 160: return 1; ! 161: ! 162: if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) ! 163: return 1; ! 164: ! 165: return 0; ! 166: } ! 167: ! 168: /* Accept any constant that can be moved in one instructions into a ! 169: general register. */ ! 170: int ! 171: cint_ok_for_move (intval) ! 172: int intval; ! 173: { ! 174: /* OK if ldo, ldil, or zdepi, can be used. */ ! 175: return (VAL_14_BITS_P (intval) || (intval & RIGHT_BITS_MASK) == 0 ! 176: || zdepi_cint_p (intval)); ! 177: } ! 178: ! 179: /* Accept anything that can be moved in one instruction into a general ! 180: register. */ ! 181: int ! 182: move_operand (op, mode) ! 183: rtx op; ! 184: enum machine_mode mode; ! 185: { ! 186: if (register_operand (op, mode)) ! 187: return 1; ! 188: ! 189: if (GET_CODE (op) == CONST_INT) ! 190: return cint_ok_for_move (INTVAL (op)); ! 191: ! 192: if (GET_MODE (op) != mode) ! 193: return 0; ! 194: if (GET_CODE (op) == SUBREG) ! 195: op = SUBREG_REG (op); ! 196: if (GET_CODE (op) != MEM) ! 197: return 0; ! 198: ! 199: op = XEXP (op, 0); ! 200: if (GET_CODE (op) == LO_SUM) ! 201: return (register_operand (XEXP (op, 0), Pmode) ! 202: && CONSTANT_P (XEXP (op, 1))); ! 203: return memory_address_p (mode, op); ! 204: } ! 205: ! 206: /* Accept REG and any CONST_INT that can be moved in one instruction into a ! 207: general register. */ ! 208: int ! 209: reg_or_cint_move_operand (op, mode) ! 210: rtx op; ! 211: enum machine_mode mode; ! 212: { ! 213: if (register_operand (op, mode)) ! 214: return 1; ! 215: ! 216: if (GET_CODE (op) == CONST_INT) ! 217: return cint_ok_for_move (INTVAL (op)); ! 218: ! 219: return 0; ! 220: } ! 221: ! 222: int ! 223: pic_operand (op, mode) ! 224: rtx op; ! 225: enum machine_mode mode; ! 226: { ! 227: return flag_pic && GET_CODE (op) == LABEL_REF; ! 228: } ! 229: ! 230: int ! 231: fp_reg_operand (op, mode) ! 232: rtx op; ! 233: enum machine_mode mode; ! 234: { ! 235: return reg_renumber && FP_REG_P (op); ! 236: } ! 237: ! 238: ! 239: extern int current_function_uses_pic_offset_table; ! 240: extern rtx force_reg (), validize_mem (); ! 241: ! 242: /* The rtx for the global offset table which is a special form ! 243: that *is* a position independent symbolic constant. */ ! 244: rtx pic_pc_rtx; ! 245: ! 246: /* Ensure that we are not using patterns that are not OK with PIC. */ ! 247: ! 248: int ! 249: check_pic (i) ! 250: int i; ! 251: { ! 252: extern rtx recog_operand[]; ! 253: switch (flag_pic) ! 254: { ! 255: case 1: ! 256: if (GET_CODE (recog_operand[i]) == SYMBOL_REF ! 257: || (GET_CODE (recog_operand[i]) == CONST ! 258: && ! rtx_equal_p (pic_pc_rtx, recog_operand[i]))) ! 259: abort (); ! 260: case 2: ! 261: default: ! 262: return 1; ! 263: } ! 264: } ! 265: ! 266: /* Return truth value of whether OP can be used as an operand in a ! 267: three operand arithmetic insn that accepts registers of mode MODE ! 268: or 14-bit signed integers. */ ! 269: int ! 270: arith_operand (op, mode) ! 271: rtx op; ! 272: enum machine_mode mode; ! 273: { ! 274: return (register_operand (op, mode) ! 275: || (GET_CODE (op) == CONST_INT && INT_14_BITS (op))); ! 276: } ! 277: ! 278: /* Return truth value of whether OP can be used as an operand in a ! 279: three operand arithmetic insn that accepts registers of mode MODE ! 280: or 11-bit signed integers. */ ! 281: int ! 282: arith11_operand (op, mode) ! 283: rtx op; ! 284: enum machine_mode mode; ! 285: { ! 286: return (register_operand (op, mode) ! 287: || (GET_CODE (op) == CONST_INT && INT_11_BITS (op))); ! 288: } ! 289: ! 290: /* A constant integer suitable for use in a PRE_MODIFY memory ! 291: reference. */ ! 292: int ! 293: pre_cint_operand (op, mode) ! 294: rtx op; ! 295: enum machine_mode mode; ! 296: { ! 297: return (GET_CODE (op) == CONST_INT ! 298: && INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10); ! 299: } ! 300: ! 301: /* A constant integer suitable for use in a POST_MODIFY memory ! 302: reference. */ ! 303: int ! 304: post_cint_operand (op, mode) ! 305: rtx op; ! 306: enum machine_mode mode; ! 307: { ! 308: return (GET_CODE (op) == CONST_INT ! 309: && INTVAL (op) < 0x2000 && INTVAL (op) >= -0x10); ! 310: } ! 311: ! 312: int ! 313: arith_double_operand (op, mode) ! 314: rtx op; ! 315: enum machine_mode mode; ! 316: { ! 317: return (register_operand (op, mode) ! 318: || (GET_CODE (op) == CONST_DOUBLE ! 319: && GET_MODE (op) == mode ! 320: && VAL_14_BITS_P (CONST_DOUBLE_LOW (op)) ! 321: && (CONST_DOUBLE_HIGH (op) >= 0 ! 322: == ((CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); ! 323: } ! 324: ! 325: /* Return truth value of whether OP is a integer which fits the ! 326: range constraining immediate operands in three-address insns. */ ! 327: ! 328: int ! 329: int5_operand (op, mode) ! 330: rtx op; ! 331: enum machine_mode mode; ! 332: { ! 333: return (GET_CODE (op) == CONST_INT && INT_5_BITS (op)); ! 334: } ! 335: ! 336: int ! 337: uint5_operand (op, mode) ! 338: rtx op; ! 339: enum machine_mode mode; ! 340: { ! 341: return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op)); ! 342: } ! 343: ! 344: ! 345: int ! 346: int11_operand (op, mode) ! 347: rtx op; ! 348: enum machine_mode mode; ! 349: { ! 350: return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); ! 351: } ! 352: ! 353: int ! 354: arith5_operand (op, mode) ! 355: rtx op; ! 356: enum machine_mode mode; ! 357: { ! 358: return register_operand (op, mode) || int5_operand (op, mode); ! 359: } ! 360: ! 361: /* True iff zdepi can be used to generate this CONST_INT. */ ! 362: int ! 363: zdepi_cint_p (x) ! 364: unsigned x; ! 365: { ! 366: unsigned lsb_mask, t; ! 367: ! 368: /* This might not be obvious, but it's at least fast. ! 369: This function is critcal; we don't have the time loops would take. */ ! 370: lsb_mask = x & -x; ! 371: t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1); ! 372: /* Return true iff t is a power of two. */ ! 373: return ((t & (t - 1)) == 0); ! 374: } ! 375: ! 376: /* True iff depi or extru can be used to compute (reg & mask). */ ! 377: int ! 378: and_mask_p (mask) ! 379: unsigned mask; ! 380: { ! 381: mask = ~mask; ! 382: mask += mask & -mask; ! 383: return (mask & (mask - 1)) == 0; ! 384: } ! 385: ! 386: /* True iff depi or extru can be used to compute (reg & OP). */ ! 387: int ! 388: and_operand (op, mode) ! 389: rtx op; ! 390: enum machine_mode mode; ! 391: { ! 392: return (register_operand (op, mode) ! 393: || (GET_CODE (op) == CONST_INT && and_mask_p (INTVAL (op)))); ! 394: } ! 395: ! 396: /* True iff depi can be used to compute (reg | MASK). */ ! 397: int ! 398: ior_mask_p (mask) ! 399: unsigned mask; ! 400: { ! 401: mask += mask & -mask; ! 402: return (mask & (mask - 1)) == 0; ! 403: } ! 404: ! 405: /* True iff depi can be used to compute (reg | OP). */ ! 406: int ! 407: ior_operand (op, mode) ! 408: rtx op; ! 409: enum machine_mode mode; ! 410: { ! 411: return (GET_CODE (op) == CONST_INT && ior_mask_p (INTVAL (op))); ! 412: } ! 413: ! 414: int ! 415: lhs_lshift_operand (op, mode) ! 416: rtx op; ! 417: enum machine_mode mode; ! 418: { ! 419: return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode); ! 420: } ! 421: ! 422: /* True iff OP is a CONST_INT of the forms 0...0xxxx or 0...01...1xxxx. ! 423: Such values can be the left hand side x in (x << r), using the zvdepi ! 424: instruction. */ ! 425: int ! 426: lhs_lshift_cint_operand (op, mode) ! 427: rtx op; ! 428: enum machine_mode mode; ! 429: { ! 430: unsigned x; ! 431: if (GET_CODE (op) != CONST_INT) ! 432: return 0; ! 433: x = INTVAL (op) >> 4; ! 434: return (x & (x + 1)) == 0; ! 435: } ! 436: ! 437: int ! 438: arith32_operand (op, mode) ! 439: rtx op; ! 440: enum machine_mode mode; ! 441: { ! 442: return register_operand (op, mode) || GET_CODE (op) == CONST_INT; ! 443: } ! 444: ! 445: int ! 446: pc_or_label_operand (op, mode) ! 447: rtx op; ! 448: enum machine_mode mode; ! 449: { ! 450: return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF); ! 451: } ! 452: ! 453: int ! 454: code_label_operand (op, mode) ! 455: rtx op; ! 456: enum machine_mode mode; ! 457: { ! 458: return (GET_CODE (op) == CODE_LABEL); ! 459: } ! 460: ! 461: /* Legitimize PIC addresses. If the address is already ! 462: position-independent, we return ORIG. Newly generated ! 463: position-independent addresses go to REG. If we need more ! 464: than one register, we lose. */ ! 465: ! 466: rtx ! 467: legitimize_pic_address (orig, mode, reg) ! 468: rtx orig, reg; ! 469: enum machine_mode mode; ! 470: { ! 471: rtx pic_ref = orig; ! 472: ! 473: #ifdef MACHO_PIC ! 474: return machopic_legitimize_pic_address (orig, mode, reg); ! 475: #endif ! 476: ! 477: if (GET_CODE (orig) == SYMBOL_REF) ! 478: { ! 479: if (reg == 0) ! 480: abort (); ! 481: ! 482: if (flag_pic == 2) ! 483: { ! 484: emit_insn (gen_rtx (SET, VOIDmode, reg, ! 485: gen_rtx (HIGH, Pmode, orig))); ! 486: emit_insn (gen_rtx (SET, VOIDmode, reg, ! 487: gen_rtx (LO_SUM, Pmode, reg, orig))); ! 488: orig = reg; ! 489: } ! 490: pic_ref = gen_rtx (MEM, Pmode, ! 491: gen_rtx (PLUS, Pmode, ! 492: pic_offset_table_rtx, orig)); ! 493: current_function_uses_pic_offset_table = 1; ! 494: RTX_UNCHANGING_P (pic_ref) = 1; ! 495: emit_move_insn (reg, pic_ref); ! 496: return reg; ! 497: } ! 498: else if (GET_CODE (orig) == CONST) ! 499: { ! 500: rtx base; ! 501: ! 502: if (GET_CODE (XEXP (orig, 0)) == PLUS ! 503: && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) ! 504: return orig; ! 505: ! 506: if (reg == 0) ! 507: abort (); ! 508: ! 509: if (GET_CODE (XEXP (orig, 0)) == PLUS) ! 510: { ! 511: base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); ! 512: orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, ! 513: base == reg ? 0 : reg); ! 514: } ! 515: else abort (); ! 516: if (GET_CODE (orig) == CONST_INT) ! 517: { ! 518: if (INT_14_BITS (orig)) ! 519: return plus_constant_for_output (base, INTVAL (orig)); ! 520: orig = force_reg (Pmode, orig); ! 521: } ! 522: pic_ref = gen_rtx (PLUS, Pmode, base, orig); ! 523: /* Likewise, should we set special REG_NOTEs here? */ ! 524: } ! 525: return pic_ref; ! 526: } ! 527: ! 528: /* Set up PIC-specific rtl. This should not cause any insns ! 529: to be emitted. */ ! 530: ! 531: void ! 532: initialize_pic () ! 533: { ! 534: } ! 535: ! 536: /* Emit special PIC prologues and epilogues. */ ! 537: ! 538: void ! 539: finalize_pic () ! 540: { ! 541: rtx insn = get_insns (); ! 542: ! 543: while (GET_CODE (insn) == NOTE) ! 544: if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG) ! 545: break; ! 546: else ! 547: insn = NEXT_INSN (insn); ! 548: ! 549: ! 550: #ifdef NeXT_ASM ! 551: if (current_function_uses_pic_offset_table) ! 552: { ! 553: rtx label = gen_label_rtx (); ! 554: rtx target = gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM); ! 555: XSTR (label, 4) = machopic_function_base_name (); ! 556: insn = emit_jump_insn_after (gen_branch_and_link (label, target), insn); ! 557: insn = emit_label_after (label, insn); ! 558: LABEL_PRESERVE_P (label) = 1; ! 559: insn = emit_insn_after (gen_andsi3 (target, target, ! 560: gen_rtx (CONST_INT, SImode, ~3UL)), ! 561: insn); ! 562: } ! 563: #endif ! 564: ! 565: if (hppa_save_pic_table_rtx) ! 566: { ! 567: emit_insn_after (gen_rtx (SET, VOIDmode, ! 568: hppa_save_pic_table_rtx, ! 569: gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM)), ! 570: insn); ! 571: /* Need to emit this whether or not we obey regdecls, ! 572: since setjmp/longjmp can cause life info to screw up. */ ! 573: hppa_save_pic_table_rtx = 0; ! 574: } ! 575: ! 576: emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx)); ! 577: } ! 578: ! 579: /* Try machine-dependent ways of modifying an illegitimate address ! 580: to be legitimate. If we find one, return the new, valid address. ! 581: This macro is used in only one place: `memory_address' in explow.c. ! 582: ! 583: OLDX is the address as it was before break_out_memory_refs was called. ! 584: In some cases it is useful to look at this to decide what needs to be done. ! 585: ! 586: MODE and WIN are passed so that this macro can use ! 587: GO_IF_LEGITIMATE_ADDRESS. ! 588: ! 589: It is always safe for this macro to do nothing. It exists to recognize ! 590: opportunities to optimize the output. ! 591: ! 592: For the PA, transform: ! 593: ! 594: memory(X + <large int>) ! 595: ! 596: into: ! 597: ! 598: if (<large int> & mask) >= 16 ! 599: Y = (<large int> & ~mask) + mask + 1 Round up. ! 600: else ! 601: Y = (<large int> & ~mask) Round down. ! 602: Z = X + Y ! 603: memory (Z + (<large int> - Y)); ! 604: ! 605: This is for CSE to find several similar references, and only use one Z. ! 606: ! 607: X can either be a SYMBOL_REF or REG, but because combine can not ! 608: perform a 4->2 combination we do nothing for SYMBOL_REF + D where ! 609: D will not fit in 14 bits. ! 610: ! 611: MODE_FLOAT references allow displacements which fit in 5 bits, so use ! 612: 0x1f as the mask. ! 613: ! 614: MODE_INT references allow displacements which fit in 14 bits, so use ! 615: 0x3fff as the mask. ! 616: ! 617: This relies on the fact that most mode MODE_FLOAT references will use FP ! 618: registers and most mode MODE_INT references will use integer registers. ! 619: (In the rare case of an FP register used in an integer MODE, we depend ! 620: on secondary reloads to clean things up.) ! 621: ! 622: ! 623: It is also beneficial to handle (plus (mult (X) (Y)) (Z)) in a special ! 624: manner if Y is 2, 4, or 8. (allows more shadd insns and shifted indexed ! 625: adressing modes to be used). ! 626: ! 627: Put X and Z into registers. Then put the entire expression into ! 628: a register. */ ! 629: ! 630: rtx ! 631: hppa_legitimize_address (x, oldx, mode) ! 632: rtx x, oldx; ! 633: enum machine_mode mode; ! 634: { ! 635: ! 636: rtx orig = x; ! 637: ! 638: /* Strip off CONST. */ ! 639: if (GET_CODE (x) == CONST) ! 640: x = XEXP (x, 0); ! 641: ! 642: if (GET_CODE (x) == PLUS ! 643: && GET_CODE (XEXP (x, 1)) == CONST_INT ! 644: && (GET_CODE (XEXP (x, 0)) == SYMBOL_REF ! 645: || GET_CODE (XEXP (x, 0)) == REG)) ! 646: { ! 647: rtx int_part, ptr_reg; ! 648: int newoffset; ! 649: int offset = INTVAL (XEXP (x, 1)); ! 650: int mask = GET_MODE_CLASS (mode) == MODE_FLOAT ? 0x1f : 0x3fff; ! 651: ! 652: /* Choose which way to round the offset. Round up if we ! 653: are >= halfway to the next boundary. */ ! 654: if ((offset & mask) >= ((mask + 1) / 2)) ! 655: newoffset = (offset & ~ mask) + mask + 1; ! 656: else ! 657: newoffset = (offset & ~ mask); ! 658: ! 659: /* If the newoffset will not fit in 14 bits (ldo), then ! 660: handling this would take 4 or 5 instructions (2 to load ! 661: the SYMBOL_REF + 1 or 2 to load the newoffset + 1 to ! 662: add the new offset and the SYMBOL_REF.) Combine can ! 663: not handle 4->2 or 5->2 combinations, so do not create ! 664: them. */ ! 665: if (! VAL_14_BITS_P (newoffset) ! 666: && GET_CODE (XEXP (x, 0)) == SYMBOL_REF) ! 667: { ! 668: rtx const_part = gen_rtx (CONST, VOIDmode, ! 669: gen_rtx (PLUS, Pmode, ! 670: XEXP (x, 0), ! 671: GEN_INT (newoffset))); ! 672: rtx tmp_reg ! 673: = force_reg (Pmode, ! 674: gen_rtx (HIGH, Pmode, const_part)); ! 675: ptr_reg ! 676: = force_reg (Pmode, ! 677: gen_rtx (LO_SUM, Pmode, ! 678: tmp_reg, const_part)); ! 679: } ! 680: else ! 681: { ! 682: if (! VAL_14_BITS_P (newoffset)) ! 683: int_part = force_reg (Pmode, GEN_INT (newoffset)); ! 684: else ! 685: int_part = GEN_INT (newoffset); ! 686: ! 687: ptr_reg = force_reg (Pmode, ! 688: gen_rtx (PLUS, Pmode, ! 689: force_reg (Pmode, XEXP (x, 0)), ! 690: int_part)); ! 691: } ! 692: return plus_constant (ptr_reg, offset - newoffset); ! 693: } ! 694: ! 695: /* Try to arrange things so that indexing modes can be used, but ! 696: only do so if indexing is safe. ! 697: ! 698: Indexing is safe when the second operand for the outer PLUS ! 699: is a REG, SUBREG, SYMBOL_REF or the like. ! 700: ! 701: For 2.5, indexing is also safe for (plus (symbol_ref) (const_int)) ! 702: if the integer is > 0. */ ! 703: if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT ! 704: && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT ! 705: && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))) ! 706: && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o' ! 707: || GET_CODE (XEXP (x, 1)) == SUBREG) ! 708: && GET_CODE (XEXP (x, 1)) != CONST) ! 709: { ! 710: int val = INTVAL (XEXP (XEXP (x, 0), 1)); ! 711: rtx reg1, reg2; ! 712: reg1 = force_reg (Pmode, force_operand (XEXP (x, 1), 0)); ! 713: reg2 = force_reg (Pmode, ! 714: force_operand (XEXP (XEXP (x, 0), 0), 0)); ! 715: return force_reg (Pmode, ! 716: gen_rtx (PLUS, Pmode, ! 717: gen_rtx (MULT, Pmode, reg2, ! 718: GEN_INT (val)), ! 719: reg1)); ! 720: } ! 721: ! 722: /* Uh-oh. We might have an address for x[n-100000]. This needs ! 723: special handling. */ ! 724: ! 725: if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT ! 726: && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT ! 727: && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))) ! 728: { ! 729: /* Ugly. We modify things here so that the address offset specified ! 730: by the index expression is computed first, then added to x to form ! 731: the entire address. ! 732: ! 733: For 2.5, it might be profitable to set things up so that we ! 734: compute the raw (unscaled) index first, then use scaled indexing ! 735: to access memory, or better yet have the MI parts of the compiler ! 736: handle this. */ ! 737: ! 738: rtx regx1, regy1, regy2, y; ! 739: ! 740: /* Strip off any CONST. */ ! 741: y = XEXP (x, 1); ! 742: if (GET_CODE (y) == CONST) ! 743: y = XEXP (y, 0); ! 744: ! 745: if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS) ! 746: { ! 747: regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0)); ! 748: regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0)); ! 749: regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0)); ! 750: regx1 = force_reg (Pmode, gen_rtx (GET_CODE (y), Pmode, regx1, regy2)); ! 751: return force_reg (Pmode, gen_rtx (PLUS, Pmode, regx1, regy1)); ! 752: } ! 753: } ! 754: ! 755: if (flag_pic) ! 756: return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); ! 757: ! 758: return orig; ! 759: } ! 760: ! 761: /* For the HPPA, REG and REG+CONST is cost 0 ! 762: and addresses involving symbolic constants are cost 2. ! 763: ! 764: PIC addresses are very expensive. ! 765: ! 766: It is no coincidence that this has the same structure ! 767: as GO_IF_LEGITIMATE_ADDRESS. */ ! 768: int ! 769: hppa_address_cost (X) ! 770: rtx X; ! 771: { ! 772: if (GET_CODE (X) == PLUS) ! 773: return 1; ! 774: else if (GET_CODE (X) == LO_SUM) ! 775: return 1; ! 776: else if (GET_CODE (X) == HIGH) ! 777: return 2; ! 778: return 4; ! 779: } ! 780: ! 781: /* Emit insns to move operands[1] into operands[0]. ! 782: ! 783: Return 1 if we have written out everything that needs to be done to ! 784: do the move. Otherwise, return 0 and the caller will emit the move ! 785: normally. */ ! 786: ! 787: int ! 788: emit_move_sequence (operands, mode, scratch_reg) ! 789: rtx *operands; ! 790: enum machine_mode mode; ! 791: rtx scratch_reg; ! 792: { ! 793: register rtx operand0 = operands[0]; ! 794: register rtx operand1 = operands[1]; ! 795: ! 796: /* Handle secondary reloads for loads/stores of FP registers from ! 797: REG+D addresses where D does not fit in 5 bits. */ ! 798: if (fp_reg_operand (operand0, mode) ! 799: && GET_CODE (operand1) == MEM ! 800: /* Using DFmode forces only short displacements be be ! 801: recognized as valid in reg+d addressing modes. */ ! 802: && ! memory_address_p (DFmode, XEXP (operand1, 0)) ! 803: && scratch_reg) ! 804: { ! 805: emit_move_insn (scratch_reg, XEXP (operand1 , 0)); ! 806: emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (MEM, mode, ! 807: scratch_reg))); ! 808: return 1; ! 809: } ! 810: else if (fp_reg_operand (operand1, mode) ! 811: && GET_CODE (operand0) == MEM ! 812: /* Using DFmode forces only short displacements be be ! 813: recognized as valid in reg+d addressing modes. */ ! 814: && ! memory_address_p (DFmode, XEXP (operand0, 0)) ! 815: && scratch_reg) ! 816: { ! 817: emit_move_insn (scratch_reg, XEXP (operand0 , 0)); ! 818: emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (MEM, mode, scratch_reg), ! 819: operand1)); ! 820: return 1; ! 821: } ! 822: /* Handle secondary reloads for loads of FP registers from constant ! 823: expressions by forcing the constant into memory. ! 824: ! 825: use scratch_reg to hold the address of the memory location. ! 826: ! 827: ??? The proper fix is to change PREFERRED_RELOAD_CLASS to return ! 828: NO_REGS when presented with a const_int and an register class ! 829: containing only FP registers. Doing so unfortunately creates ! 830: more problems than it solves. Fix this for 2.5. */ ! 831: else if (fp_reg_operand (operand0, mode) ! 832: && CONSTANT_P (operand1) ! 833: && scratch_reg) ! 834: { ! 835: rtx xoperands[2]; ! 836: ! 837: /* Force the constant into memory and put the address of the ! 838: memory location into scratch_reg. */ ! 839: xoperands[0] = scratch_reg; ! 840: xoperands[1] = XEXP (force_const_mem (mode, operand1), 0); ! 841: emit_move_sequence (xoperands, Pmode, 0); ! 842: ! 843: /* Now load the destination register. */ ! 844: emit_insn (gen_rtx (SET, mode, operand0, ! 845: gen_rtx (MEM, mode, scratch_reg))); ! 846: return 1; ! 847: } ! 848: /* Handle secondary reloads for SAR. These occur when trying to load ! 849: the SAR from memory or from a FP register. */ ! 850: else if (GET_CODE (operand0) == REG ! 851: && REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS ! 852: && (GET_CODE (operand1) == MEM ! 853: || (GET_CODE (operand1) == REG ! 854: && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))) ! 855: && scratch_reg) ! 856: { ! 857: emit_move_insn (scratch_reg, operand1); ! 858: emit_move_insn (operand0, scratch_reg); ! 859: return 1; ! 860: } ! 861: /* Handle most common case: storing into a register. */ ! 862: else if (register_operand (operand0, mode)) ! 863: { ! 864: if (register_operand (operand1, mode) ! 865: || (GET_CODE (operand1) == CONST_INT && INT_14_BITS (operand1)) ! 866: || (operand1 == CONST0_RTX (mode)) ! 867: || (GET_CODE (operand1) == HIGH ! 868: && !symbolic_operand (XEXP (operand1, 0))) ! 869: /* Only `general_operands' can come here, so MEM is ok. */ ! 870: || GET_CODE (operand1) == MEM) ! 871: { ! 872: /* Run this case quickly. */ ! 873: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 874: return 1; ! 875: } ! 876: } ! 877: else if (GET_CODE (operand0) == MEM) ! 878: { ! 879: if (register_operand (operand1, mode) || operand1 == CONST0_RTX (mode)) ! 880: { ! 881: /* Run this case quickly. */ ! 882: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 883: return 1; ! 884: } ! 885: if (! (reload_in_progress || reload_completed)) ! 886: { ! 887: operands[0] = validize_mem (operand0); ! 888: operands[1] = operand1 = force_reg (mode, operand1); ! 889: } ! 890: } ! 891: ! 892: /* Simplify the source if we need to. */ ! 893: if ((GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) ! 894: || (GET_CODE (operand1) == HIGH ! 895: && symbolic_operand (XEXP (operand1, 0), mode))) ! 896: { ! 897: int ishighonly = 0; ! 898: ! 899: if (GET_CODE (operand1) == HIGH) ! 900: { ! 901: ishighonly = 1; ! 902: operand1 = XEXP (operand1, 0); ! 903: } ! 904: if (symbolic_operand (operand1, mode)) ! 905: { ! 906: if (flag_pic) ! 907: { ! 908: rtx temp; ! 909: ! 910: if (reload_in_progress || reload_completed) ! 911: temp = operand0; ! 912: else ! 913: temp = gen_reg_rtx (Pmode); ! 914: ! 915: operands[1] = legitimize_pic_address (operand1, mode, temp); ! 916: emit_insn (gen_rtx (SET, VOIDmode, operand0, operands[1])); ! 917: } ! 918: /* On the HPPA, references to data space are supposed to */ ! 919: /* use dp, register 27, but showing it in the RTL inhibits various ! 920: cse and loop optimizations. */ ! 921: else ! 922: { ! 923: rtx temp, set; ! 924: ! 925: if (reload_in_progress || reload_completed) ! 926: temp = scratch_reg ? scratch_reg : operand0; ! 927: else ! 928: temp = gen_reg_rtx (mode); ! 929: ! 930: if (ishighonly) ! 931: set = gen_rtx (SET, mode, operand0, temp); ! 932: else ! 933: set = gen_rtx (SET, VOIDmode, ! 934: operand0, ! 935: gen_rtx (LO_SUM, mode, temp, operand1)); ! 936: ! 937: emit_insn (gen_rtx (SET, VOIDmode, ! 938: temp, ! 939: gen_rtx (HIGH, mode, operand1))); ! 940: if (function_label_operand (operand1, mode)) ! 941: { ! 942: rtx temp; ! 943: ! 944: if (reload_in_progress || reload_completed) ! 945: temp = scratch_reg; ! 946: else ! 947: temp = gen_reg_rtx (mode); ! 948: ! 949: if (!temp) ! 950: abort (); ! 951: emit_insn (gen_rtx (PARALLEL, VOIDmode, ! 952: gen_rtvec (2, ! 953: set, ! 954: gen_rtx (CLOBBER, VOIDmode, ! 955: temp)))); ! 956: } ! 957: else ! 958: emit_insn (set); ! 959: return 1; ! 960: } ! 961: return 1; ! 962: } ! 963: else if (GET_CODE (operand1) != CONST_INT ! 964: || ! cint_ok_for_move (INTVAL (operand1))) ! 965: { ! 966: rtx temp; ! 967: ! 968: if (reload_in_progress || reload_completed) ! 969: temp = operand0; ! 970: else ! 971: temp = gen_reg_rtx (mode); ! 972: ! 973: emit_insn (gen_rtx (SET, VOIDmode, temp, ! 974: gen_rtx (HIGH, mode, operand1))); ! 975: operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); ! 976: } ! 977: } ! 978: /* Now have insn-emit do whatever it normally does. */ ! 979: return 0; ! 980: } ! 981: ! 982: /* Does operand (which is a symbolic_operand) live in text space? If ! 983: so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true. */ ! 984: ! 985: int ! 986: read_only_operand (operand) ! 987: rtx operand; ! 988: { ! 989: if (GET_CODE (operand) == CONST) ! 990: operand = XEXP (XEXP (operand, 0), 0); ! 991: if (GET_CODE (operand) == SYMBOL_REF) ! 992: return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand); ! 993: return 1; ! 994: } ! 995: ! 996: ! 997: /* Return the best assembler insn template ! 998: for moving operands[1] into operands[0] as a fullword. */ ! 999: char * ! 1000: singlemove_string (operands) ! 1001: rtx *operands; ! 1002: { ! 1003: if (GET_CODE (operands[0]) == MEM) ! 1004: return "stw %r1,%0"; ! 1005: else if (GET_CODE (operands[1]) == MEM) ! 1006: return "ldw %1,%0"; ! 1007: else if (GET_CODE (operands[1]) == CONST_DOUBLE ! 1008: && GET_MODE (operands[1]) == SFmode) ! 1009: { ! 1010: int i; ! 1011: union real_extract u; ! 1012: union float_extract { float f; int i; } v; ! 1013: ! 1014: bcopy (&CONST_DOUBLE_LOW (operands[1]), &u, sizeof u); ! 1015: v.f = REAL_VALUE_TRUNCATE (SFmode, u.d); ! 1016: i = v.i; ! 1017: ! 1018: operands[1] = gen_rtx (CONST_INT, VOIDmode, i); ! 1019: ! 1020: /* See if we can handle this constant in a single instruction. */ ! 1021: if (cint_ok_for_move (INTVAL (operands[1]))) ! 1022: { ! 1023: int intval = INTVAL (operands[1]); ! 1024: ! 1025: if (intval == 0) ! 1026: return "copy 0,%0"; ! 1027: else if (VAL_14_BITS_P (intval)) ! 1028: return "ldi %1,%0"; ! 1029: else if ((intval & RIGHT_BITS_MASK) == 0) ! 1030: return "ldil L%'%1,%0"; ! 1031: else if (zdepi_cint_p (intval)) ! 1032: return "zdepi %Z1,%0"; ! 1033: } ! 1034: else ! 1035: return "ldil L%'%1,%0\n\tldo R%'%1(%0),%0"; ! 1036: } ! 1037: ! 1038: else if (GET_CODE (operands[1]) == CONST_INT) ! 1039: { ! 1040: /* See if we can handle this in a single instruction. */ ! 1041: if (cint_ok_for_move (INTVAL (operands[1]))) ! 1042: { ! 1043: int intval = INTVAL (operands[1]); ! 1044: ! 1045: if (intval == 0) ! 1046: return "copy 0,%0"; ! 1047: else if (VAL_14_BITS_P (intval)) ! 1048: return "ldi %1,%0"; ! 1049: else if ((intval & RIGHT_BITS_MASK) == 0) ! 1050: return "ldil L%'%1,%0"; ! 1051: else if (zdepi_cint_p (intval)) ! 1052: return "zdepi %Z1,%0"; ! 1053: } ! 1054: else ! 1055: return "ldil L%'%1,%0\n\tldo R%'%1(%0),%0"; ! 1056: } ! 1057: return "copy %1,%0"; ! 1058: } ! 1059: ! 1060: ! 1061: /* Compute position (in OP[1]) and width (in OP[2]) ! 1062: useful for copying IMM to a register using the zdepi ! 1063: instructions. Store the immediate value to insert in OP[0]. */ ! 1064: void ! 1065: compute_zdepi_operands (imm, op) ! 1066: unsigned imm; ! 1067: unsigned *op; ! 1068: { ! 1069: int lsb, len; ! 1070: ! 1071: /* Find the least significant set bit in IMM. */ ! 1072: for (lsb = 0; lsb < 32; lsb++) ! 1073: { ! 1074: if ((imm & 1) != 0) ! 1075: break; ! 1076: imm >>= 1; ! 1077: } ! 1078: ! 1079: /* Choose variants based on *sign* of the 5-bit field. */ ! 1080: if ((imm & 0x10) == 0) ! 1081: len = (lsb <= 28) ? 4 : 32 - lsb; ! 1082: else ! 1083: { ! 1084: /* Find the width of the bitstring in IMM. */ ! 1085: for (len = 5; len < 32; len++) ! 1086: { ! 1087: if ((imm & (1 << len)) == 0) ! 1088: break; ! 1089: } ! 1090: ! 1091: /* Sign extend IMM as a 5-bit value. */ ! 1092: imm = (imm & 0xf) - 0x10; ! 1093: } ! 1094: ! 1095: op[0] = imm; ! 1096: op[1] = 31 - lsb; ! 1097: op[2] = len; ! 1098: } ! 1099: ! 1100: /* Output assembler code to perform a doubleword move insn ! 1101: with operands OPERANDS. */ ! 1102: ! 1103: char * ! 1104: output_move_double (operands) ! 1105: rtx *operands; ! 1106: { ! 1107: enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1; ! 1108: rtx latehalf[2]; ! 1109: rtx addreg0 = 0, addreg1 = 0; ! 1110: ! 1111: /* First classify both operands. */ ! 1112: ! 1113: if (REG_P (operands[0])) ! 1114: optype0 = REGOP; ! 1115: else if (offsettable_memref_p (operands[0])) ! 1116: optype0 = OFFSOP; ! 1117: else if (GET_CODE (operands[0]) == MEM) ! 1118: optype0 = MEMOP; ! 1119: else ! 1120: optype0 = RNDOP; ! 1121: ! 1122: if (REG_P (operands[1])) ! 1123: optype1 = REGOP; ! 1124: else if (CONSTANT_P (operands[1])) ! 1125: optype1 = CNSTOP; ! 1126: else if (offsettable_memref_p (operands[1])) ! 1127: optype1 = OFFSOP; ! 1128: else if (GET_CODE (operands[1]) == MEM) ! 1129: optype1 = MEMOP; ! 1130: else ! 1131: optype1 = RNDOP; ! 1132: ! 1133: /* Check for the cases that the operand constraints are not ! 1134: supposed to allow to happen. Abort if we get one, ! 1135: because generating code for these cases is painful. */ ! 1136: ! 1137: if (optype0 != REGOP && optype1 != REGOP) ! 1138: abort (); ! 1139: ! 1140: /* Handle auto decrementing and incrementing loads and stores ! 1141: specifically, since the structure of the function doesn't work ! 1142: for them without major modification. Do it better when we learn ! 1143: this port about the general inc/dec addressing of PA. ! 1144: (This was written by tege. Chide him if it doesn't work.) */ ! 1145: ! 1146: if (optype0 == MEMOP) ! 1147: { ! 1148: /* We have to output the address syntax ourselves, since print_operand ! 1149: doesn't deal with the addresses we want to use. Fix this later. */ ! 1150: ! 1151: rtx addr = XEXP (operands[0], 0); ! 1152: if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC) ! 1153: { ! 1154: rtx high_reg = gen_rtx (SUBREG, SImode, operands[1], 0); ! 1155: ! 1156: operands[0] = XEXP (addr, 0); ! 1157: if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) ! 1158: abort (); ! 1159: ! 1160: if (!reg_overlap_mentioned_p (high_reg, addr)) ! 1161: { ! 1162: /* No overlap between high target register and address ! 1163: register. (We do this in a non-obvious way to ! 1164: save a register file writeback) */ ! 1165: if (GET_CODE (addr) == POST_INC) ! 1166: return "stws,ma %1,8(0,%0)\n\tstw %R1,-4(0,%0)"; ! 1167: return "stws,ma %1,-8(0,%0)\n\tstw %R1,12(0,%0)"; ! 1168: } ! 1169: else ! 1170: abort(); ! 1171: } ! 1172: else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC) ! 1173: { ! 1174: rtx high_reg = gen_rtx (SUBREG, SImode, operands[1], 0); ! 1175: ! 1176: operands[0] = XEXP (addr, 0); ! 1177: if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) ! 1178: abort (); ! 1179: ! 1180: if (!reg_overlap_mentioned_p (high_reg, addr)) ! 1181: { ! 1182: /* No overlap between high target register and address ! 1183: register. (We do this in a non-obvious way to ! 1184: save a register file writeback) */ ! 1185: if (GET_CODE (addr) == PRE_INC) ! 1186: return "stws,mb %1,8(0,%0)\n\tstw %R1,4(0,%0)"; ! 1187: return "stws,mb %1,-8(0,%0)\n\tstw %R1,4(0,%0)"; ! 1188: } ! 1189: else ! 1190: abort(); ! 1191: } ! 1192: } ! 1193: if (optype1 == MEMOP) ! 1194: { ! 1195: /* We have to output the address syntax ourselves, since print_operand ! 1196: doesn't deal with the addresses we want to use. Fix this later. */ ! 1197: ! 1198: rtx addr = XEXP (operands[1], 0); ! 1199: if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC) ! 1200: { ! 1201: rtx high_reg = gen_rtx (SUBREG, SImode, operands[0], 0); ! 1202: ! 1203: operands[1] = XEXP (addr, 0); ! 1204: if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) ! 1205: abort (); ! 1206: ! 1207: if (!reg_overlap_mentioned_p (high_reg, addr)) ! 1208: { ! 1209: /* No overlap between high target register and address ! 1210: register. (We do this in a non-obvious way to ! 1211: save a register file writeback) */ ! 1212: if (GET_CODE (addr) == POST_INC) ! 1213: return "ldws,ma 8(0,%1),%0\n\tldw -4(0,%1),%R0"; ! 1214: return "ldws,ma -8(0,%1),%0\n\tldw 12(0,%1),%R0"; ! 1215: } ! 1216: else ! 1217: { ! 1218: /* This is an undefined situation. We should load into the ! 1219: address register *and* update that register. Probably ! 1220: we don't need to handle this at all. */ ! 1221: if (GET_CODE (addr) == POST_INC) ! 1222: return "ldw 4(0,%1),%R0\n\tldws,ma 8(0,%1),%0"; ! 1223: return "ldw 4(0,%1),%R0\n\tldws,ma -8(0,%1),%0"; ! 1224: } ! 1225: } ! 1226: else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC) ! 1227: { ! 1228: rtx high_reg = gen_rtx (SUBREG, SImode, operands[0], 0); ! 1229: ! 1230: operands[1] = XEXP (addr, 0); ! 1231: if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) ! 1232: abort (); ! 1233: ! 1234: if (!reg_overlap_mentioned_p (high_reg, addr)) ! 1235: { ! 1236: /* No overlap between high target register and address ! 1237: register. (We do this in a non-obvious way to ! 1238: save a register file writeback) */ ! 1239: if (GET_CODE (addr) == PRE_INC) ! 1240: return "ldws,mb 8(0,%1),%0\n\tldw 4(0,%1),%R0"; ! 1241: return "ldws,mb -8(0,%1),%0\n\tldw 4(0,%1),%R0"; ! 1242: } ! 1243: else ! 1244: { ! 1245: /* This is an undefined situation. We should load into the ! 1246: address register *and* update that register. Probably ! 1247: we don't need to handle this at all. */ ! 1248: if (GET_CODE (addr) == PRE_INC) ! 1249: return "ldw 12(0,%1),%R0\n\tldws,mb 8(0,%1),%0"; ! 1250: return "ldw -4(0,%1),%R0\n\tldws,mb -8(0,%1),%0"; ! 1251: } ! 1252: } ! 1253: } ! 1254: ! 1255: /* If an operand is an unoffsettable memory ref, find a register ! 1256: we can increment temporarily to make it refer to the second word. */ ! 1257: ! 1258: if (optype0 == MEMOP) ! 1259: addreg0 = find_addr_reg (XEXP (operands[0], 0)); ! 1260: ! 1261: if (optype1 == MEMOP) ! 1262: addreg1 = find_addr_reg (XEXP (operands[1], 0)); ! 1263: ! 1264: /* Ok, we can do one word at a time. ! 1265: Normally we do the low-numbered word first. ! 1266: ! 1267: In either case, set up in LATEHALF the operands to use ! 1268: for the high-numbered word and in some cases alter the ! 1269: operands in OPERANDS to be suitable for the low-numbered word. */ ! 1270: ! 1271: if (optype0 == REGOP) ! 1272: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 1273: else if (optype0 == OFFSOP) ! 1274: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 1275: else ! 1276: latehalf[0] = operands[0]; ! 1277: ! 1278: if (optype1 == REGOP) ! 1279: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 1280: else if (optype1 == OFFSOP) ! 1281: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 1282: else if (optype1 == CNSTOP) ! 1283: split_double (operands[1], &operands[1], &latehalf[1]); ! 1284: else ! 1285: latehalf[1] = operands[1]; ! 1286: ! 1287: /* If the first move would clobber the source of the second one, ! 1288: do them in the other order. ! 1289: ! 1290: RMS says "This happens only for registers; ! 1291: such overlap can't happen in memory unless the user explicitly ! 1292: sets it up, and that is an undefined circumstance." ! 1293: ! 1294: but it happens on the HP-PA when loading parameter registers, ! 1295: so I am going to define that circumstance, and make it work ! 1296: as expected. */ ! 1297: ! 1298: if (optype0 == REGOP && (optype1 == MEMOP || optype1 == OFFSOP) ! 1299: && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0))) ! 1300: { ! 1301: /* XXX THIS PROBABLY DOESN'T WORK. */ ! 1302: /* Do the late half first. */ ! 1303: if (addreg1) ! 1304: output_asm_insn ("ldo 4(%0),%0", &addreg1); ! 1305: output_asm_insn (singlemove_string (latehalf), latehalf); ! 1306: if (addreg1) ! 1307: output_asm_insn ("ldo -4(%0),%0", &addreg1); ! 1308: /* Then clobber. */ ! 1309: return singlemove_string (operands); ! 1310: } ! 1311: ! 1312: if (optype0 == REGOP && optype1 == REGOP ! 1313: && REGNO (operands[0]) == REGNO (operands[1]) + 1) ! 1314: { ! 1315: output_asm_insn (singlemove_string (latehalf), latehalf); ! 1316: return singlemove_string (operands); ! 1317: } ! 1318: ! 1319: /* Normal case: do the two words, low-numbered first. */ ! 1320: ! 1321: output_asm_insn (singlemove_string (operands), operands); ! 1322: ! 1323: /* Make any unoffsettable addresses point at high-numbered word. */ ! 1324: if (addreg0) ! 1325: output_asm_insn ("ldo 4(%0),%0", &addreg0); ! 1326: if (addreg1) ! 1327: output_asm_insn ("ldo 4(%0),%0", &addreg1); ! 1328: ! 1329: /* Do that word. */ ! 1330: output_asm_insn (singlemove_string (latehalf), latehalf); ! 1331: ! 1332: /* Undo the adds we just did. */ ! 1333: if (addreg0) ! 1334: output_asm_insn ("ldo -4(%0),%0", &addreg0); ! 1335: if (addreg1) ! 1336: output_asm_insn ("ldo -4(%0),%0", &addreg1); ! 1337: ! 1338: return ""; ! 1339: } ! 1340: ! 1341: char * ! 1342: output_fp_move_double (operands) ! 1343: rtx *operands; ! 1344: { ! 1345: if (FP_REG_P (operands[0])) ! 1346: { ! 1347: if (FP_REG_P (operands[1]) ! 1348: || operands[1] == CONST0_RTX (GET_MODE (operands[0]))) ! 1349: output_asm_insn ("fcpy,dbl %r1,%0", operands); ! 1350: else ! 1351: output_asm_insn ("fldds%F1 %1,%0", operands); ! 1352: } ! 1353: else if (FP_REG_P (operands[1])) ! 1354: { ! 1355: output_asm_insn ("fstds%F0 %1,%0", operands); ! 1356: } ! 1357: else if (operands[1] == CONST0_RTX (GET_MODE (operands[0]))) ! 1358: { ! 1359: if (GET_CODE (operands[0]) == REG) ! 1360: { ! 1361: rtx xoperands[2]; ! 1362: xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 1363: xoperands[0] = operands[0]; ! 1364: output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands); ! 1365: } ! 1366: /* This is a pain. You have to be prepared to deal with an ! 1367: arbritary address here including pre/post increment/decrement. ! 1368: ! 1369: so avoid this in the MD. */ ! 1370: else ! 1371: abort (); ! 1372: } ! 1373: else abort (); ! 1374: return ""; ! 1375: } ! 1376: ! 1377: /* Return a REG that occurs in ADDR with coefficient 1. ! 1378: ADDR can be effectively incremented by incrementing REG. */ ! 1379: ! 1380: static rtx ! 1381: find_addr_reg (addr) ! 1382: rtx addr; ! 1383: { ! 1384: while (GET_CODE (addr) == PLUS) ! 1385: { ! 1386: if (GET_CODE (XEXP (addr, 0)) == REG) ! 1387: addr = XEXP (addr, 0); ! 1388: else if (GET_CODE (XEXP (addr, 1)) == REG) ! 1389: addr = XEXP (addr, 1); ! 1390: else if (CONSTANT_P (XEXP (addr, 0))) ! 1391: addr = XEXP (addr, 1); ! 1392: else if (CONSTANT_P (XEXP (addr, 1))) ! 1393: addr = XEXP (addr, 0); ! 1394: else ! 1395: abort (); ! 1396: } ! 1397: if (GET_CODE (addr) == REG) ! 1398: return addr; ! 1399: abort (); ! 1400: } ! 1401: ! 1402: /* Emit code to perform a block move. ! 1403: ! 1404: Restriction: If the length argument is non-constant, alignment ! 1405: must be 4. ! 1406: ! 1407: OPERANDS[0] is the destination pointer as a REG, clobbered. ! 1408: OPERANDS[1] is the source pointer as a REG, clobbered. ! 1409: if SIZE_IS_CONSTANT ! 1410: OPERANDS[2] is a register for temporary storage. ! 1411: OPERANDS[4] is the size as a CONST_INT ! 1412: else ! 1413: OPERANDS[2] is a REG which will contain the size, clobbered. ! 1414: OPERANDS[3] is a register for temporary storage. ! 1415: OPERANDS[5] is the alignment safe to use, as a CONST_INT. */ ! 1416: ! 1417: char * ! 1418: output_block_move (operands, size_is_constant) ! 1419: rtx *operands; ! 1420: int size_is_constant; ! 1421: { ! 1422: int align = INTVAL (operands[5]); ! 1423: unsigned long n_bytes; ! 1424: ! 1425: /* We can't move more than four bytes at a time because the PA ! 1426: has no longer integer move insns. (Could use fp mem ops?) */ ! 1427: if (align > 4) ! 1428: align = 4; ! 1429: ! 1430: if (size_is_constant) ! 1431: { ! 1432: unsigned long offset; ! 1433: rtx temp; ! 1434: ! 1435: n_bytes = INTVAL (operands[4]); ! 1436: if (n_bytes == 0) ! 1437: return ""; ! 1438: ! 1439: if (align >= 4) ! 1440: { ! 1441: /* Don't unroll too large blocks. */ ! 1442: if (n_bytes > 64) ! 1443: goto copy_with_loop; ! 1444: ! 1445: /* Read and store using two registers, and hide latency ! 1446: by deferring the stores until three instructions after ! 1447: the corresponding load. The last load insn will read ! 1448: the entire word were the last bytes are, possibly past ! 1449: the end of the source block, but since loads are aligned, ! 1450: this is harmless. */ ! 1451: ! 1452: output_asm_insn ("ldws,ma 4(0,%1),%2", operands); ! 1453: ! 1454: for (offset = 4; offset < n_bytes; offset += 4) ! 1455: { ! 1456: output_asm_insn ("ldws,ma 4(0,%1),%3", operands); ! 1457: output_asm_insn ("stws,ma %2,4(0,%0)", operands); ! 1458: ! 1459: temp = operands[2]; ! 1460: operands[2] = operands[3]; ! 1461: operands[3] = temp; ! 1462: } ! 1463: if (n_bytes % 4 == 0) ! 1464: /* Store the last word. */ ! 1465: output_asm_insn ("stw %2,0(0,%0)", operands); ! 1466: else ! 1467: { ! 1468: /* Store the last, partial word. */ ! 1469: operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes % 4); ! 1470: output_asm_insn ("stbys,e %2,%4(0,%0)", operands); ! 1471: } ! 1472: return ""; ! 1473: } ! 1474: ! 1475: if (align >= 2 && n_bytes >= 2) ! 1476: { ! 1477: output_asm_insn ("ldhs,ma 2(0,%1),%2", operands); ! 1478: ! 1479: for (offset = 2; offset + 2 <= n_bytes; offset += 2) ! 1480: { ! 1481: output_asm_insn ("ldhs,ma 2(0,%1),%3", operands); ! 1482: output_asm_insn ("sths,ma %2,2(0,%0)", operands); ! 1483: ! 1484: temp = operands[2]; ! 1485: operands[2] = operands[3]; ! 1486: operands[3] = temp; ! 1487: } ! 1488: if (n_bytes % 2 != 0) ! 1489: output_asm_insn ("ldb 0(0,%1),%3", operands); ! 1490: ! 1491: output_asm_insn ("sths,ma %2,2(0,%0)", operands); ! 1492: ! 1493: if (n_bytes % 2 != 0) ! 1494: output_asm_insn ("stb %3,0(0,%0)", operands); ! 1495: ! 1496: return ""; ! 1497: } ! 1498: ! 1499: output_asm_insn ("ldbs,ma 1(0,%1),%2", operands); ! 1500: ! 1501: for (offset = 1; offset + 1 <= n_bytes; offset += 1) ! 1502: { ! 1503: output_asm_insn ("ldbs,ma 1(0,%1),%3", operands); ! 1504: output_asm_insn ("stbs,ma %2,1(0,%0)", operands); ! 1505: ! 1506: temp = operands[2]; ! 1507: operands[2] = operands[3]; ! 1508: operands[3] = temp; ! 1509: } ! 1510: output_asm_insn ("stb %2,0(0,%0)", operands); ! 1511: ! 1512: return ""; ! 1513: } ! 1514: ! 1515: if (align != 4) ! 1516: abort(); ! 1517: ! 1518: copy_with_loop: ! 1519: ! 1520: if (size_is_constant) ! 1521: { ! 1522: /* Size is compile-time determined, and also not ! 1523: very small (such small cases are handled above). */ ! 1524: operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes - 4); ! 1525: output_asm_insn ("ldo %4(0),%2", operands); ! 1526: } ! 1527: else ! 1528: { ! 1529: /* Decrement counter by 4, and if it becomes negative, jump past the ! 1530: word copying loop. */ ! 1531: output_asm_insn ("addib,<,n -4,%2,.+16", operands); ! 1532: } ! 1533: ! 1534: /* Copying loop. Note that the first load is in the annulled delay slot ! 1535: of addib. Is it OK on PA to have a load in a delay slot, i.e. is a ! 1536: possible page fault stopped in time? */ ! 1537: output_asm_insn ("ldws,ma 4(0,%1),%3", operands); ! 1538: output_asm_insn ("addib,>= -4,%2,.-4", operands); ! 1539: output_asm_insn ("stws,ma %3,4(0,%0)", operands); ! 1540: ! 1541: /* The counter is negative, >= -4. The remaining number of bytes are ! 1542: determined by the two least significant bits. */ ! 1543: ! 1544: if (size_is_constant) ! 1545: { ! 1546: if (n_bytes % 4 != 0) ! 1547: { ! 1548: /* Read the entire word of the source block tail. */ ! 1549: output_asm_insn ("ldw 0(0,%1),%3", operands); ! 1550: operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes % 4); ! 1551: output_asm_insn ("stbys,e %3,%4(0,%0)", operands); ! 1552: } ! 1553: } ! 1554: else ! 1555: { ! 1556: /* Add 4 to counter. If it becomes zero, we're done. */ ! 1557: output_asm_insn ("addib,=,n 4,%2,.+16", operands); ! 1558: ! 1559: /* Read the entire word of the source block tail. (Also this ! 1560: load is in an annulled delay slot.) */ ! 1561: output_asm_insn ("ldw 0(0,%1),%3", operands); ! 1562: ! 1563: /* Make %0 point at the first byte after the destination block. */ ! 1564: output_asm_insn ("add %2,%0,%0", operands); ! 1565: /* Store the leftmost bytes, up to, but not including, the address ! 1566: in %0. */ ! 1567: output_asm_insn ("stbys,e %3,0(0,%0)", operands); ! 1568: } ! 1569: return ""; ! 1570: } ! 1571: ! 1572: /* Count the number of insns necessary to handle this block move. ! 1573: ! 1574: Basic structure is the same as emit_block_move, except that we ! 1575: count insns rather than emit them. */ ! 1576: ! 1577: int ! 1578: compute_movstrsi_length (insn) ! 1579: rtx insn; ! 1580: { ! 1581: rtx pat = PATTERN (insn); ! 1582: int size_is_constant; ! 1583: int align = INTVAL (XEXP (XVECEXP (pat, 0, 6), 0)); ! 1584: unsigned long n_bytes; ! 1585: int insn_count = 0; ! 1586: ! 1587: if (GET_CODE (XEXP (XVECEXP (pat, 0, 5), 0)) == CONST_INT) ! 1588: { ! 1589: size_is_constant = 1; ! 1590: n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 5), 0)); ! 1591: } ! 1592: else ! 1593: { ! 1594: size_is_constant = 0; ! 1595: n_bytes = 0; ! 1596: } ! 1597: ! 1598: /* We can't move more than four bytes at a time because the PA ! 1599: has no longer integer move insns. (Could use fp mem ops?) */ ! 1600: if (align > 4) ! 1601: align = 4; ! 1602: ! 1603: if (size_is_constant) ! 1604: { ! 1605: unsigned long offset; ! 1606: ! 1607: if (n_bytes == 0) ! 1608: return 0; ! 1609: ! 1610: if (align >= 4) ! 1611: { ! 1612: /* Don't unroll too large blocks. */ ! 1613: if (n_bytes > 64) ! 1614: goto copy_with_loop; ! 1615: ! 1616: /* first load */ ! 1617: insn_count = 1; ! 1618: ! 1619: /* Count the unrolled insns. */ ! 1620: for (offset = 4; offset < n_bytes; offset += 4) ! 1621: insn_count += 2; ! 1622: ! 1623: /* Count last store or partial store. */ ! 1624: insn_count += 1; ! 1625: return insn_count * 4; ! 1626: } ! 1627: ! 1628: if (align >= 2 && n_bytes >= 2) ! 1629: { ! 1630: /* initial load. */ ! 1631: insn_count = 1; ! 1632: ! 1633: /* Unrolled loop. */ ! 1634: for (offset = 2; offset + 2 <= n_bytes; offset += 2) ! 1635: insn_count += 2; ! 1636: ! 1637: /* ??? odd load/store */ ! 1638: if (n_bytes % 2 != 0) ! 1639: insn_count += 2; ! 1640: ! 1641: /* ??? final store from loop. */ ! 1642: insn_count += 1; ! 1643: ! 1644: return insn_count * 4; ! 1645: } ! 1646: ! 1647: /* First load. */ ! 1648: insn_count = 1; ! 1649: ! 1650: /* The unrolled loop. */ ! 1651: for (offset = 1; offset + 1 <= n_bytes; offset += 1) ! 1652: insn_count += 2; ! 1653: ! 1654: /* Final store. */ ! 1655: insn_count += 1; ! 1656: ! 1657: return insn_count * 4; ! 1658: } ! 1659: ! 1660: if (align != 4) ! 1661: abort(); ! 1662: ! 1663: copy_with_loop: ! 1664: ! 1665: /* setup for constant and non-constant case. */ ! 1666: insn_count = 1; ! 1667: ! 1668: /* The copying loop. */ ! 1669: insn_count += 3; ! 1670: ! 1671: /* The counter is negative, >= -4. The remaining number of bytes are ! 1672: determined by the two least significant bits. */ ! 1673: ! 1674: if (size_is_constant) ! 1675: { ! 1676: if (n_bytes % 4 != 0) ! 1677: insn_count += 2; ! 1678: } ! 1679: else ! 1680: insn_count += 4; ! 1681: return insn_count * 4; ! 1682: } ! 1683: ! 1684: ! 1685: char * ! 1686: output_and (operands) ! 1687: rtx *operands; ! 1688: { ! 1689: if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0) ! 1690: { ! 1691: unsigned mask = INTVAL (operands[2]); ! 1692: int ls0, ls1, ms0, p, len; ! 1693: ! 1694: for (ls0 = 0; ls0 < 32; ls0++) ! 1695: if ((mask & (1 << ls0)) == 0) ! 1696: break; ! 1697: ! 1698: for (ls1 = ls0; ls1 < 32; ls1++) ! 1699: if ((mask & (1 << ls1)) != 0) ! 1700: break; ! 1701: ! 1702: for (ms0 = ls1; ms0 < 32; ms0++) ! 1703: if ((mask & (1 << ms0)) == 0) ! 1704: break; ! 1705: ! 1706: if (ms0 != 32) ! 1707: abort(); ! 1708: ! 1709: if (ls1 == 32) ! 1710: { ! 1711: len = ls0; ! 1712: ! 1713: if (len == 0) ! 1714: abort (); ! 1715: ! 1716: operands[2] = gen_rtx (CONST_INT, VOIDmode, len); ! 1717: return "extru %1,31,%2,%0"; ! 1718: } ! 1719: else ! 1720: { ! 1721: /* We could use this `depi' for the case above as well, but `depi' ! 1722: requires one more register file access than an `extru'. */ ! 1723: ! 1724: p = 31 - ls0; ! 1725: len = ls1 - ls0; ! 1726: ! 1727: operands[2] = gen_rtx (CONST_INT, VOIDmode, p); ! 1728: operands[3] = gen_rtx (CONST_INT, VOIDmode, len); ! 1729: return "depi 0,%2,%3,%0"; ! 1730: } ! 1731: } ! 1732: else ! 1733: return "and %1,%2,%0"; ! 1734: } ! 1735: ! 1736: char * ! 1737: output_ior (operands) ! 1738: rtx *operands; ! 1739: { ! 1740: unsigned mask = INTVAL (operands[2]); ! 1741: int bs0, bs1, p, len; ! 1742: ! 1743: if (INTVAL (operands[2]) == 0) ! 1744: return "copy %1,%0"; ! 1745: ! 1746: for (bs0 = 0; bs0 < 32; bs0++) ! 1747: if ((mask & (1 << bs0)) != 0) ! 1748: break; ! 1749: ! 1750: for (bs1 = bs0; bs1 < 32; bs1++) ! 1751: if ((mask & (1 << bs1)) == 0) ! 1752: break; ! 1753: ! 1754: if (bs1 != 32 && ((unsigned) 1 << bs1) <= mask) ! 1755: abort(); ! 1756: ! 1757: p = 31 - bs0; ! 1758: len = bs1 - bs0; ! 1759: ! 1760: operands[2] = gen_rtx (CONST_INT, VOIDmode, p); ! 1761: operands[3] = gen_rtx (CONST_INT, VOIDmode, len); ! 1762: return "depi -1,%2,%3,%0"; ! 1763: } ! 1764: ! 1765: /* Output an ascii string. */ ! 1766: void ! 1767: output_ascii (file, p, size) ! 1768: FILE *file; ! 1769: unsigned char *p; ! 1770: int size; ! 1771: { ! 1772: int i; ! 1773: int chars_output; ! 1774: unsigned char partial_output[16]; /* Max space 4 chars can occupy. */ ! 1775: ! 1776: /* The HP assembler can only take strings of 256 characters at one ! 1777: time. This is a limitation on input line length, *not* the ! 1778: length of the string. Sigh. Even worse, it seems that the ! 1779: restriction is in number of input characters (see \xnn & ! 1780: \whatever). So we have to do this very carefully. */ ! 1781: ! 1782: fprintf (file, "\t.%s \"", STRING_SECTION_NAME); ! 1783: ! 1784: chars_output = 0; ! 1785: for (i = 0; i < size; i += 4) ! 1786: { ! 1787: int co = 0; ! 1788: int io = 0; ! 1789: for (io = 0, co = 0; io < MIN (4, size - i); io++) ! 1790: { ! 1791: register unsigned int c = p[i + io]; ! 1792: ! 1793: if (c == '\"' || c == '\\') ! 1794: partial_output[co++] = '\\'; ! 1795: ! 1796: if (c >= ' ' && c < 0177) ! 1797: partial_output[co++] = c; ! 1798: ! 1799: else if (TARGET_GAS) ! 1800: { ! 1801: /* output octal numbers for gas */ ! 1802: partial_output[co++] = '\\'; ! 1803: partial_output[co++] = (c / 64) % 8 - 0 + '0'; ! 1804: partial_output[co++] = (c / 8) % 8 - 0 + '0'; ! 1805: partial_output[co++] = c % 8 - 0 + '0'; ! 1806: } ! 1807: else ! 1808: { ! 1809: unsigned int hexd; ! 1810: partial_output[co++] = '\\'; ! 1811: partial_output[co++] = 'x'; ! 1812: hexd = c / 16 - 0 + '0'; ! 1813: if (hexd > '9') ! 1814: hexd -= '9' - 'a' + 1; ! 1815: partial_output[co++] = hexd; ! 1816: hexd = c % 16 - 0 + '0'; ! 1817: if (hexd > '9') ! 1818: hexd -= '9' - 'a' + 1; ! 1819: partial_output[co++] = hexd; ! 1820: } ! 1821: } ! 1822: if (chars_output + co > 243) ! 1823: { ! 1824: fprintf (file, "\"\n\t.%s \"", STRING_SECTION_NAME); ! 1825: chars_output = 0; ! 1826: } ! 1827: fwrite (partial_output, 1, co, file); ! 1828: chars_output += co; ! 1829: co = 0; ! 1830: } ! 1831: fprintf (file, "\"\n"); ! 1832: } ! 1833: ! 1834: /* You may have trouble believing this, but this is the HP-PA stack ! 1835: layout. Wow. ! 1836: ! 1837: Offset Contents ! 1838: ! 1839: Variable arguments (optional; any number may be allocated) ! 1840: ! 1841: SP-(4*(N+9)) arg word N ! 1842: : : ! 1843: SP-56 arg word 5 ! 1844: SP-52 arg word 4 ! 1845: ! 1846: Fixed arguments (must be allocated; may remain unused) ! 1847: ! 1848: SP-48 arg word 3 ! 1849: SP-44 arg word 2 ! 1850: SP-40 arg word 1 ! 1851: SP-36 arg word 0 ! 1852: ! 1853: Frame Marker ! 1854: ! 1855: SP-32 External Data Pointer (DP) ! 1856: SP-28 External sr4 ! 1857: SP-24 External/stub RP (RP') ! 1858: SP-20 Current RP ! 1859: SP-16 Static Link ! 1860: SP-12 Clean up ! 1861: SP-8 Calling Stub RP (RP'') ! 1862: SP-4 Previous SP ! 1863: ! 1864: Top of Frame ! 1865: ! 1866: SP-0 Stack Pointer (points to next available address) ! 1867: ! 1868: */ ! 1869: ! 1870: /* This function saves registers as follows. Registers marked with ' are ! 1871: this function's registers (as opposed to the previous function's). ! 1872: If a frame_pointer isn't needed, r4 is saved as a general register; ! 1873: the space for the frame pointer is still allocated, though, to keep ! 1874: things simple. ! 1875: ! 1876: ! 1877: Top of Frame ! 1878: ! 1879: SP (FP') Previous FP ! 1880: SP + 4 Alignment filler (sigh) ! 1881: SP + 8 Space for locals reserved here. ! 1882: . ! 1883: . ! 1884: . ! 1885: SP + n All call saved register used. ! 1886: . ! 1887: . ! 1888: . ! 1889: SP + o All call saved fp registers used. ! 1890: . ! 1891: . ! 1892: . ! 1893: SP + p (SP') points to next available address. ! 1894: ! 1895: */ ! 1896: ! 1897: /* Emit RTL to store REG at the memory location specified by BASE+DISP. ! 1898: Handle case where DISP > 8k by using the add_high_const pattern. ! 1899: ! 1900: Note in DISP > 8k case, we will leave the high part of the address ! 1901: in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ ! 1902: static void ! 1903: store_reg (reg, disp, base) ! 1904: int reg, disp, base; ! 1905: { ! 1906: if (VAL_14_BITS_P (disp)) ! 1907: { ! 1908: emit_move_insn (gen_rtx (MEM, SImode, ! 1909: gen_rtx (PLUS, SImode, ! 1910: gen_rtx (REG, SImode, base), ! 1911: GEN_INT (disp))), ! 1912: gen_rtx (REG, SImode, reg)); ! 1913: } ! 1914: else ! 1915: { ! 1916: emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), ! 1917: gen_rtx (REG, SImode, base), ! 1918: GEN_INT (disp))); ! 1919: emit_move_insn (gen_rtx (MEM, SImode, ! 1920: gen_rtx (LO_SUM, SImode, ! 1921: gen_rtx (REG, SImode, 1), ! 1922: GEN_INT (disp))), ! 1923: gen_rtx (REG, SImode, reg)); ! 1924: } ! 1925: } ! 1926: ! 1927: /* Emit RTL to load REG from the memory location specified by BASE+DISP. ! 1928: Handle case where DISP > 8k by using the add_high_const pattern. ! 1929: ! 1930: Note in DISP > 8k case, we will leave the high part of the address ! 1931: in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ ! 1932: static void ! 1933: load_reg (reg, disp, base) ! 1934: int reg, disp, base; ! 1935: { ! 1936: if (VAL_14_BITS_P (disp)) ! 1937: { ! 1938: emit_move_insn (gen_rtx (REG, SImode, reg), ! 1939: gen_rtx (MEM, SImode, ! 1940: gen_rtx (PLUS, SImode, ! 1941: gen_rtx (REG, SImode, base), ! 1942: GEN_INT (disp)))); ! 1943: ! 1944: } ! 1945: else ! 1946: { ! 1947: emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), ! 1948: gen_rtx (REG, SImode, base), ! 1949: GEN_INT (disp))); ! 1950: emit_move_insn (gen_rtx (REG, SImode, reg), ! 1951: gen_rtx (MEM, SImode, ! 1952: gen_rtx (LO_SUM, SImode, ! 1953: gen_rtx (REG, SImode, 1), ! 1954: GEN_INT (disp)))); ! 1955: } ! 1956: } ! 1957: ! 1958: /* Emit RTL to set REG to the value specified by BASE+DISP. ! 1959: Handle case where DISP > 8k by using the add_high_const pattern. ! 1960: ! 1961: Note in DISP > 8k case, we will leave the high part of the address ! 1962: in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/ ! 1963: static void ! 1964: set_reg_plus_d(reg, base, disp) ! 1965: int reg, base, disp; ! 1966: { ! 1967: if (VAL_14_BITS_P (disp)) ! 1968: { ! 1969: emit_move_insn (gen_rtx (REG, SImode, reg), ! 1970: gen_rtx (PLUS, SImode, ! 1971: gen_rtx (REG, SImode, base), ! 1972: GEN_INT (disp))); ! 1973: ! 1974: } ! 1975: else ! 1976: { ! 1977: emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), ! 1978: gen_rtx (REG, SImode, base), ! 1979: GEN_INT (disp))); ! 1980: emit_move_insn (gen_rtx (REG, SImode, reg), ! 1981: gen_rtx (LO_SUM, SImode, ! 1982: gen_rtx (REG, SImode, 1), ! 1983: GEN_INT (disp))); ! 1984: } ! 1985: } ! 1986: ! 1987: /* Global variables set by FUNCTION_PROLOGUE. */ ! 1988: /* Size of frame. Need to know this to emit return insns from ! 1989: leaf procedures. */ ! 1990: static int actual_fsize; ! 1991: static int local_fsize, save_fregs; ! 1992: ! 1993: int ! 1994: compute_frame_size (size, fregs_live) ! 1995: int size; ! 1996: int *fregs_live; ! 1997: { ! 1998: extern int current_function_outgoing_args_size; ! 1999: int i, fsize; ! 2000: ! 2001: /* 8 is space for frame pointer + filler. If any frame is allocated ! 2002: we need to add this in because of STARTING_FRAME_OFFSET. */ ! 2003: fsize = size + (size || frame_pointer_needed ? 8 : 0); ! 2004: ! 2005: /* fp is stored in a special place. */ ! 2006: if (frame_pointer_needed) ! 2007: { ! 2008: for (i = 18; i >= 5; i--) ! 2009: if (regs_ever_live[i]) ! 2010: fsize += 4; ! 2011: ! 2012: if (regs_ever_live[3]) ! 2013: fsize += 4; ! 2014: } ! 2015: else ! 2016: { ! 2017: for (i = 18; i >= 3; i--) ! 2018: if (regs_ever_live[i]) ! 2019: fsize += 4; ! 2020: } ! 2021: fsize = (fsize + 7) & ~7; ! 2022: ! 2023: if (!TARGET_SNAKE) ! 2024: { ! 2025: for (i = 43; i >= 40; i--) ! 2026: if (regs_ever_live[i]) ! 2027: { ! 2028: fsize += 8; ! 2029: if (fregs_live) ! 2030: *fregs_live = 1; ! 2031: } ! 2032: } ! 2033: else ! 2034: { ! 2035: for (i = 78; i >= 60; i -= 2) ! 2036: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 2037: { ! 2038: fsize += 8; ! 2039: if (fregs_live) ! 2040: *fregs_live = 1; ! 2041: } ! 2042: } ! 2043: fsize += current_function_outgoing_args_size; ! 2044: if (! leaf_function_p () || fsize) ! 2045: fsize += 32; ! 2046: return (fsize + 63) & ~63; ! 2047: } ! 2048: ! 2049: rtx hp_profile_label_rtx; ! 2050: static char hp_profile_label_name[8]; ! 2051: void ! 2052: output_function_prologue (file, size) ! 2053: FILE *file; ! 2054: int size; ! 2055: { ! 2056: ! 2057: /* hppa_expand_prologue does the dirty work now. We just need ! 2058: to output the assembler directives which denote the start ! 2059: of a function. */ ! 2060: fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=%d", actual_fsize); ! 2061: if (regs_ever_live[2] || profile_flag) ! 2062: fprintf (file, ",CALLS,SAVE_RP"); ! 2063: else ! 2064: fprintf (file, ",NO_CALLS"); ! 2065: ! 2066: if (frame_pointer_needed) ! 2067: fprintf (file, ",SAVE_SP"); ! 2068: ! 2069: /* Pass on information about the number of callee register saves ! 2070: performed in the prologue. ! 2071: ! 2072: The compiler is supposed to pass the highest register number ! 2073: saved, the assembler then has to adjust that number before ! 2074: entering it into the unwind descriptor (to account for any ! 2075: caller saved registers with lower register numbers than the ! 2076: first callee saved register). */ ! 2077: if (gr_saved) ! 2078: fprintf (file, ",ENTRY_GR=%d", gr_saved + 2); ! 2079: ! 2080: if (fr_saved) ! 2081: fprintf (file, ",ENTRY_FR=%d", fr_saved + 11); ! 2082: ! 2083: fprintf (file, "\n\t.ENTRY\n"); ! 2084: ! 2085: /* Horrid hack. emit_function_prologue will modify this RTL in ! 2086: place to get the expected results. */ ! 2087: if (profile_flag) ! 2088: sprintf(hp_profile_label_name, "LP$%04d", hp_profile_labelno); ! 2089: } ! 2090: ! 2091: void ! 2092: hppa_expand_prologue() ! 2093: { ! 2094: ! 2095: extern char call_used_regs[]; ! 2096: int size = get_frame_size (); ! 2097: int merge_sp_adjust_with_store = 0; ! 2098: int i, offset; ! 2099: rtx tmpreg, size_rtx; ! 2100: ! 2101: ! 2102: gr_saved = 0; ! 2103: fr_saved = 0; ! 2104: save_fregs = 0; ! 2105: local_fsize = size + (size || frame_pointer_needed ? 8 : 0); ! 2106: actual_fsize = compute_frame_size (size, &save_fregs); ! 2107: ! 2108: /* Compute a few things we will use often. */ ! 2109: tmpreg = gen_rtx (REG, SImode, 1); ! 2110: size_rtx = GEN_INT (actual_fsize); ! 2111: ! 2112: /* Save RP first. The calling conventions manual states RP will ! 2113: always be stored into the caller's frame at sp-20. */ ! 2114: if (regs_ever_live[2] || profile_flag) ! 2115: store_reg (2, -20, STACK_POINTER_REGNUM); ! 2116: ! 2117: /* Allocate the local frame and set up the frame pointer if needed. */ ! 2118: if (actual_fsize) ! 2119: if (frame_pointer_needed) ! 2120: { ! 2121: /* Copy the old frame pointer temporarily into %r1. Set up the ! 2122: new stack pointer, then store away the saved old frame pointer ! 2123: into the stack at sp+actual_fsize and at the same time update ! 2124: the stack pointer by actual_fsize bytes. Two versions, first ! 2125: handles small (<8k) frames. The second handles large (>8k) ! 2126: frames. */ ! 2127: emit_move_insn (tmpreg, frame_pointer_rtx); ! 2128: emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); ! 2129: if (VAL_14_BITS_P (actual_fsize)) ! 2130: emit_insn (gen_post_stwm (stack_pointer_rtx, ! 2131: stack_pointer_rtx, ! 2132: size_rtx, tmpreg)); ! 2133: else ! 2134: { ! 2135: store_reg (1, 0, FRAME_POINTER_REGNUM); ! 2136: set_reg_plus_d (STACK_POINTER_REGNUM, ! 2137: STACK_POINTER_REGNUM, ! 2138: actual_fsize); ! 2139: } ! 2140: } ! 2141: /* no frame pointer needed. */ ! 2142: else ! 2143: { ! 2144: /* In some cases we can perform the first callee register save ! 2145: and allocating the stack frame at the same time. If so, just ! 2146: make a note of it and defer allocating the frame until saving ! 2147: the callee registers. */ ! 2148: if (VAL_14_BITS_P (-actual_fsize) ! 2149: && local_fsize == 0 ! 2150: && ! profile_flag ! 2151: && ! flag_pic) ! 2152: merge_sp_adjust_with_store = 1; ! 2153: /* Can not optimize. Adjust the stack frame by actual_fsize bytes. */ ! 2154: else if (actual_fsize != 0) ! 2155: set_reg_plus_d (STACK_POINTER_REGNUM, ! 2156: STACK_POINTER_REGNUM, ! 2157: actual_fsize); ! 2158: } ! 2159: /* The hppa calling conventions say that that %r19, the pic offset ! 2160: register, is saved at sp - 32 (in this function's frame) when ! 2161: generating PIC code. */ ! 2162: if (flag_pic) ! 2163: { ! 2164: #ifndef NeXT_ASM ! 2165: store_reg (PIC_OFFSET_TABLE_REGNUM, -32, STACK_POINTER_REGNUM); ! 2166: #endif ! 2167: } ! 2168: ! 2169: ! 2170: /* Profiling code. ! 2171: ! 2172: Instead of taking one argument, the counter label, as most normal ! 2173: mcounts do, _mcount appears to behave differently on the HPPA. It ! 2174: takes the return address of the caller, the address of this routine, ! 2175: and the address of the label. Also, it isn't magic, so ! 2176: argument registre hsave to be preserved. */ ! 2177: if (profile_flag) ! 2178: { ! 2179: int pc_offset, i, arg_offset, basereg, offsetadj; ! 2180: ! 2181: pc_offset = 4 + (frame_pointer_needed ! 2182: ? (VAL_14_BITS_P (actual_fsize) ? 12 : 20) ! 2183: : (VAL_14_BITS_P (actual_fsize) ? 4 : 8)); ! 2184: ! 2185: /* When the function has a frame pointer, use it as the base ! 2186: register for saving/restore registers. Else use the stack ! 2187: pointer. Adjust the offset according to the frame size if ! 2188: this function does not have a frame pointer. */ ! 2189: ! 2190: basereg = frame_pointer_needed ? FRAME_POINTER_REGNUM ! 2191: : STACK_POINTER_REGNUM; ! 2192: offsetadj = frame_pointer_needed ? 0 : actual_fsize; ! 2193: ! 2194: /* Horrid hack. emit_function_prologue will modify this RTL in ! 2195: place to get the expected results. sprintf here is just to ! 2196: put something in the name. */ ! 2197: sprintf(hp_profile_label_name, "LP%d", profile_label_no); ! 2198: hp_profile_label_rtx = gen_rtx (SYMBOL_REF, SImode, ! 2199: hp_profile_label_name); ! 2200: if (current_function_returns_struct) ! 2201: store_reg (STRUCT_VALUE_REGNUM, - 12 - offsetadj, basereg); ! 2202: ! 2203: for (i = 26, arg_offset = -36 - offsetadj; i >= 23; i--, arg_offset -= 4) ! 2204: if (regs_ever_live [i]) ! 2205: { ! 2206: store_reg (i, arg_offset, basereg); ! 2207: /* Deal with arg_offset not fitting in 14 bits. */ ! 2208: pc_offset += VAL_14_BITS_P (arg_offset) ? 4 : 8; ! 2209: } ! 2210: ! 2211: emit_move_insn (gen_rtx (REG, SImode, 26), gen_rtx (REG, SImode, 2)); ! 2212: #ifndef NeXT_ASM ! 2213: emit_move_insn (tmpreg, gen_rtx (HIGH, SImode, hp_profile_label_rtx)); ! 2214: emit_move_insn (gen_rtx (REG, SImode, 24), ! 2215: gen_rtx (LO_SUM, SImode, tmpreg, hp_profile_label_rtx)); ! 2216: #endif ! 2217: /* %r25 is set from within the output pattern. */ ! 2218: emit_insn (gen_call_profiler (GEN_INT (- pc_offset - 20))); ! 2219: ! 2220: /* Restore argument registers. */ ! 2221: for (i = 26, arg_offset = -36 - offsetadj; i >= 23; i--, arg_offset -= 4) ! 2222: if (regs_ever_live [i]) ! 2223: load_reg (i, arg_offset, basereg); ! 2224: ! 2225: if (current_function_returns_struct) ! 2226: load_reg (STRUCT_VALUE_REGNUM, -12 - offsetadj, basereg); ! 2227: ! 2228: } ! 2229: ! 2230: /* Normal register save. ! 2231: ! 2232: Do not save the frame pointer in the frame_pointer_needed case. It ! 2233: was done earlier. */ ! 2234: if (frame_pointer_needed) ! 2235: { ! 2236: for (i = 18, offset = local_fsize; i >= 3; i--) ! 2237: if (regs_ever_live[i] && ! call_used_regs[i] ! 2238: && i != FRAME_POINTER_REGNUM) ! 2239: { ! 2240: store_reg (i, offset, FRAME_POINTER_REGNUM); ! 2241: offset += 4; ! 2242: gr_saved++; ! 2243: } ! 2244: /* Account for %r4 which is saved in a special place. */ ! 2245: gr_saved++; ! 2246: } ! 2247: /* No frame pointer needed. */ ! 2248: else ! 2249: { ! 2250: for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--) ! 2251: if (regs_ever_live[i] && ! call_used_regs[i]) ! 2252: { ! 2253: /* If merge_sp_adjust_with_store is nonzero, then we can ! 2254: optimize the first GR save. */ ! 2255: if (merge_sp_adjust_with_store) ! 2256: { ! 2257: merge_sp_adjust_with_store = 0; ! 2258: emit_insn (gen_post_stwm (stack_pointer_rtx, ! 2259: stack_pointer_rtx, ! 2260: GEN_INT (-offset), ! 2261: gen_rtx (REG, SImode, i))); ! 2262: } ! 2263: else ! 2264: store_reg (i, offset, STACK_POINTER_REGNUM); ! 2265: offset += 4; ! 2266: gr_saved++; ! 2267: } ! 2268: ! 2269: /* If we wanted to merge the SP adjustment with a GR save, but we never ! 2270: did any GR saves, then just emit the adjustment here. */ ! 2271: if (merge_sp_adjust_with_store) ! 2272: set_reg_plus_d (STACK_POINTER_REGNUM, ! 2273: STACK_POINTER_REGNUM, ! 2274: actual_fsize); ! 2275: } ! 2276: ! 2277: ! 2278: /* Align pointer properly (doubleword boundary). */ ! 2279: offset = (offset + 7) & ~7; ! 2280: ! 2281: /* Floating point register store. */ ! 2282: if (save_fregs) ! 2283: { ! 2284: ! 2285: /* First get the frame or stack pointer to the start of the FP register ! 2286: save area. */ ! 2287: if (frame_pointer_needed) ! 2288: set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset); ! 2289: else ! 2290: set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); ! 2291: ! 2292: /* Now actually save the FP registers. */ ! 2293: if (! TARGET_SNAKE) ! 2294: { ! 2295: for (i = 43; i >= 40; i--) ! 2296: if (regs_ever_live[i]) ! 2297: { ! 2298: emit_move_insn (gen_rtx (MEM, DFmode, ! 2299: gen_rtx (POST_INC, DFmode, tmpreg)), ! 2300: gen_rtx (REG, DFmode, i)); ! 2301: fr_saved++; ! 2302: } ! 2303: } ! 2304: else ! 2305: { ! 2306: for (i = 78; i >= 60; i -= 2) ! 2307: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 2308: { ! 2309: emit_move_insn (gen_rtx (MEM, DFmode, ! 2310: gen_rtx (POST_INC, DFmode, tmpreg)), ! 2311: gen_rtx (REG, DFmode, i)); ! 2312: fr_saved++; ! 2313: } ! 2314: } ! 2315: } ! 2316: } ! 2317: ! 2318: ! 2319: void ! 2320: output_function_epilogue (file, size) ! 2321: FILE *file; ! 2322: int size; ! 2323: { ! 2324: ! 2325: rtx insn = get_last_insn (); ! 2326: ! 2327: /* hppa_expand_epilogue does the dirty work now. We just need ! 2328: to output the assembler directives which denote the end ! 2329: of a function. ! 2330: ! 2331: To make debuggers happy, emit a nop if the epilogue was completely ! 2332: eliminated due to a volatile call as the last insn in the ! 2333: current function. That way the return address (in %r2) will ! 2334: always point to a valid instruction in the current function. */ ! 2335: ! 2336: /* Get the last real insn. */ ! 2337: if (GET_CODE (insn) == NOTE) ! 2338: insn = prev_real_insn (insn); ! 2339: ! 2340: /* If it is a sequence, then look inside. */ ! 2341: if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) ! 2342: insn = XVECEXP (PATTERN (insn), 0, 0); ! 2343: ! 2344: /* If insn is a CALL_INSN, then it must be a call to a volatile ! 2345: function (otherwise there would be epilogue insns). */ ! 2346: if (insn && GET_CODE (insn) == CALL_INSN) ! 2347: fprintf (file, "\tnop\n"); ! 2348: ! 2349: fprintf (file, "\t.EXIT\n\t.PROCEND\n"); ! 2350: } ! 2351: ! 2352: void ! 2353: hppa_expand_epilogue () ! 2354: { ! 2355: rtx tmpreg; ! 2356: int offset,i; ! 2357: int merge_sp_adjust_with_load = 0; ! 2358: ! 2359: /* We will use this often. */ ! 2360: tmpreg = gen_rtx (REG, SImode, 1); ! 2361: ! 2362: /* Try to restore RP early to avoid load/use interlocks when ! 2363: RP gets used in the return (bv) instruction. This appears to still ! 2364: be necessary even when we schedule the prologue and epilogue. */ ! 2365: if (frame_pointer_needed ! 2366: && (regs_ever_live [2] || profile_flag)) ! 2367: load_reg (2, -20, FRAME_POINTER_REGNUM); ! 2368: ! 2369: /* No frame pointer, and stack is smaller than 8k. */ ! 2370: else if (! frame_pointer_needed ! 2371: && VAL_14_BITS_P (actual_fsize + 20) ! 2372: && (regs_ever_live[2] || profile_flag)) ! 2373: load_reg (2, - (actual_fsize + 20), STACK_POINTER_REGNUM); ! 2374: ! 2375: /* General register restores. */ ! 2376: if (frame_pointer_needed) ! 2377: { ! 2378: for (i = 18, offset = local_fsize; i >= 3; i--) ! 2379: if (regs_ever_live[i] && ! call_used_regs[i] ! 2380: && i != FRAME_POINTER_REGNUM) ! 2381: { ! 2382: load_reg (i, offset, FRAME_POINTER_REGNUM); ! 2383: offset += 4; ! 2384: } ! 2385: } ! 2386: else ! 2387: { ! 2388: for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--) ! 2389: if (regs_ever_live[i] && ! call_used_regs[i]) ! 2390: { ! 2391: /* Only for the first load. ! 2392: merge_sp_adjust_with_load holds the register load ! 2393: with which we will merge the sp adjustment. */ ! 2394: if (VAL_14_BITS_P (actual_fsize + 20) ! 2395: && local_fsize == 0 ! 2396: && ! merge_sp_adjust_with_load) ! 2397: merge_sp_adjust_with_load = i; ! 2398: else ! 2399: load_reg (i, offset, STACK_POINTER_REGNUM); ! 2400: offset += 4; ! 2401: } ! 2402: } ! 2403: ! 2404: /* Align pointer properly (doubleword boundary). */ ! 2405: offset = (offset + 7) & ~7; ! 2406: ! 2407: /* FP register restores. */ ! 2408: if (save_fregs) ! 2409: { ! 2410: /* Adjust the register to index off of. */ ! 2411: if (frame_pointer_needed) ! 2412: set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset); ! 2413: else ! 2414: set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); ! 2415: ! 2416: /* Actually do the restores now. */ ! 2417: if (! TARGET_SNAKE) ! 2418: { ! 2419: for (i = 43; i >= 40; i--) ! 2420: if (regs_ever_live[i]) ! 2421: emit_move_insn (gen_rtx (REG, DFmode, i), ! 2422: gen_rtx (MEM, DFmode, ! 2423: gen_rtx (POST_INC, DFmode, tmpreg))); ! 2424: ! 2425: } ! 2426: else ! 2427: { ! 2428: for (i = 78; i >= 60; i -= 2) ! 2429: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 2430: emit_move_insn (gen_rtx (REG, DFmode, i), ! 2431: gen_rtx (MEM, DFmode, ! 2432: gen_rtx (POST_INC, DFmode, tmpreg))); ! 2433: } ! 2434: } ! 2435: ! 2436: /* No frame pointer, but we have a stack greater than 8k. We restore ! 2437: %r2 very late in this case. (All other cases are restored as early ! 2438: as possible.) */ ! 2439: if (! frame_pointer_needed ! 2440: && ! VAL_14_BITS_P (actual_fsize + 20) ! 2441: && (regs_ever_live[2] || profile_flag)) ! 2442: { ! 2443: set_reg_plus_d (STACK_POINTER_REGNUM, ! 2444: STACK_POINTER_REGNUM, ! 2445: - actual_fsize); ! 2446: #ifdef NeXT_ASM ! 2447: /* The calculation previously used is incorrect with the NeXT ! 2448: * assembler. We substitute a load_reg from the stack, which ! 2449: * unfortunately will prevent an instruction from being moved into ! 2450: * the delay slot of the return. ! 2451: * We could use the strategy of doing the load_reg from r1 to get rid ! 2452: * of the extra instruction, but the calculation would be significantly ! 2453: * more difficult (and subject to change). ! 2454: */ ! 2455: load_reg (2, -20, STACK_POINTER_REGNUM); ! 2456: #else ! 2457: /* Uses value left over in %r1 by set_reg_plus_d. */ ! 2458: load_reg (2, - (actual_fsize + 20 + ((- actual_fsize) & ~0x7ff)), 1); ! 2459: #endif ! 2460: } ! 2461: ! 2462: /* Reset stack pointer (and possibly frame pointer). The stack */ ! 2463: /* pointer is initially set to fp + 64 to avoid a race condition. ! 2464: ??? What race condition?!? */ ! 2465: else if (frame_pointer_needed) ! 2466: { ! 2467: /* Emit a blockage insn here to keep these insns from being moved ! 2468: to the beginning of the prologue or into the main instruction ! 2469: stream, doing so avoids some very obscure problems. */ ! 2470: emit_insn (gen_blockage ()); ! 2471: set_reg_plus_d (STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM, 64); ! 2472: emit_insn (gen_pre_ldwm (stack_pointer_rtx, stack_pointer_rtx, ! 2473: GEN_INT (-64), frame_pointer_rtx)); ! 2474: } ! 2475: /* If we were deferring a callee register restore, do it now. */ ! 2476: else if (! frame_pointer_needed && merge_sp_adjust_with_load) ! 2477: emit_insn (gen_pre_ldwm (stack_pointer_rtx, ! 2478: stack_pointer_rtx, ! 2479: GEN_INT (- actual_fsize), ! 2480: gen_rtx (REG, SImode, ! 2481: merge_sp_adjust_with_load))); ! 2482: else if (actual_fsize != 0) ! 2483: set_reg_plus_d (STACK_POINTER_REGNUM, ! 2484: STACK_POINTER_REGNUM, ! 2485: - actual_fsize); ! 2486: } ! 2487: ! 2488: /* This is only valid once reload has completed because it depends on ! 2489: knowing exactly how much (if any) frame there is and... ! 2490: ! 2491: It's only valid if there is no frame marker to de-allocate and... ! 2492: ! 2493: It's only valid if %r2 hasn't been saved into the caller's frame ! 2494: (we're not profiling and %r2 isn't live anywhere). */ ! 2495: int ! 2496: hppa_can_use_return_insn_p () ! 2497: { ! 2498: return (reload_completed ! 2499: && (compute_frame_size (get_frame_size (), 0) ? 0 : 1) ! 2500: && ! profile_flag ! 2501: && ! regs_ever_live[2] ! 2502: && ! frame_pointer_needed); ! 2503: } ! 2504: ! 2505: void ! 2506: emit_bcond_fp (code, operand0) ! 2507: enum rtx_code code; ! 2508: rtx operand0; ! 2509: { ! 2510: emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, ! 2511: gen_rtx (IF_THEN_ELSE, VOIDmode, ! 2512: gen_rtx (code, VOIDmode, ! 2513: gen_rtx (REG, CCFPmode, 0), ! 2514: const0_rtx), ! 2515: gen_rtx (LABEL_REF, VOIDmode, operand0), ! 2516: pc_rtx))); ! 2517: ! 2518: } ! 2519: ! 2520: rtx ! 2521: gen_cmp_fp (code, operand0, operand1) ! 2522: enum rtx_code code; ! 2523: rtx operand0, operand1; ! 2524: { ! 2525: return gen_rtx (SET, VOIDmode, gen_rtx (REG, CCFPmode, 0), ! 2526: gen_rtx (code, CCFPmode, operand0, operand1)); ! 2527: } ! 2528: ! 2529: /* Adjust the cost of a scheduling dependency. Return the new cost of ! 2530: a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ ! 2531: ! 2532: int ! 2533: pa_adjust_cost (insn, link, dep_insn, cost) ! 2534: rtx insn; ! 2535: rtx link; ! 2536: rtx dep_insn; ! 2537: int cost; ! 2538: { ! 2539: if (! recog_memoized (insn)) ! 2540: return 0; ! 2541: ! 2542: if (REG_NOTE_KIND (link) == 0) ! 2543: { ! 2544: /* Data dependency; DEP_INSN writes a register that INSN reads some ! 2545: cycles later. */ ! 2546: ! 2547: if (get_attr_type (insn) == TYPE_FPSTORE) ! 2548: { ! 2549: rtx pat = PATTERN (insn); ! 2550: rtx dep_pat = PATTERN (dep_insn); ! 2551: if (GET_CODE (pat) == PARALLEL) ! 2552: { ! 2553: /* This happens for the fstXs,mb patterns. */ ! 2554: pat = XVECEXP (pat, 0, 0); ! 2555: } ! 2556: if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) ! 2557: /* If this happens, we have to extend this to schedule ! 2558: optimally. Return 0 for now. */ ! 2559: return 0; ! 2560: ! 2561: if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) ! 2562: { ! 2563: if (! recog_memoized (dep_insn)) ! 2564: return 0; ! 2565: /* DEP_INSN is writing its result to the register ! 2566: being stored in the fpstore INSN. */ ! 2567: switch (get_attr_type (dep_insn)) ! 2568: { ! 2569: case TYPE_FPLOAD: ! 2570: /* This cost 3 cycles, not 2 as the md says. */ ! 2571: return cost + 1; ! 2572: ! 2573: case TYPE_FPALU: ! 2574: case TYPE_FPMUL: ! 2575: case TYPE_FPDIVSGL: ! 2576: case TYPE_FPDIVDBL: ! 2577: case TYPE_FPSQRTSGL: ! 2578: case TYPE_FPSQRTDBL: ! 2579: /* In these important cases, we save one cycle compared to ! 2580: when flop instruction feed each other. */ ! 2581: return cost - 1; ! 2582: ! 2583: default: ! 2584: return cost; ! 2585: } ! 2586: } ! 2587: } ! 2588: ! 2589: /* For other data dependencies, the default cost specified in the ! 2590: md is correct. */ ! 2591: return cost; ! 2592: } ! 2593: else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) ! 2594: { ! 2595: /* Anti dependency; DEP_INSN reads a register that INSN writes some ! 2596: cycles later. */ ! 2597: ! 2598: if (get_attr_type (insn) == TYPE_FPLOAD) ! 2599: { ! 2600: rtx pat = PATTERN (insn); ! 2601: rtx dep_pat = PATTERN (dep_insn); ! 2602: if (GET_CODE (pat) == PARALLEL) ! 2603: { ! 2604: /* This happens for the fldXs,mb patterns. */ ! 2605: pat = XVECEXP (pat, 0, 0); ! 2606: } ! 2607: if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) ! 2608: /* If this happens, we have to extend this to schedule ! 2609: optimally. Return 0 for now. */ ! 2610: return 0; ! 2611: ! 2612: if (reg_mentioned_p (SET_DEST (pat), SET_SRC (dep_pat))) ! 2613: { ! 2614: if (! recog_memoized (dep_insn)) ! 2615: return 0; ! 2616: switch (get_attr_type (dep_insn)) ! 2617: { ! 2618: case TYPE_FPALU: ! 2619: case TYPE_FPMUL: ! 2620: case TYPE_FPDIVSGL: ! 2621: case TYPE_FPDIVDBL: ! 2622: case TYPE_FPSQRTSGL: ! 2623: case TYPE_FPSQRTDBL: ! 2624: /* A fpload can't be issued until one cycle before a ! 2625: preceeding arithmetic operation has finished, if ! 2626: the target of the fpload is any of the sources ! 2627: (or destination) of the arithmetic operation. */ ! 2628: return cost - 1; ! 2629: ! 2630: default: ! 2631: return 0; ! 2632: } ! 2633: } ! 2634: } ! 2635: ! 2636: /* For other anti dependencies, the cost is 0. */ ! 2637: return 0; ! 2638: } ! 2639: ! 2640: /* For output dependencies, the cost is often one too high. */ ! 2641: return cost - 1; ! 2642: } ! 2643: ! 2644: /* Return any length adjustment needed by INSN which already has its length ! 2645: computed as LENGTH. Return zero if no adjustment is necessary. ! 2646: ! 2647: For the PA: function calls, millicode calls, and backwards short ! 2648: conditional branches with unfilled delay slots need an adjustment by +1 ! 2649: (to account for the NOP which will be inserted into the instruction stream). ! 2650: ! 2651: Also compute the length of an inline block move here as it is too ! 2652: complicated to express as a length attribute in pa.md. */ ! 2653: int ! 2654: pa_adjust_insn_length (insn, length) ! 2655: rtx insn; ! 2656: int length; ! 2657: { ! 2658: rtx pat = PATTERN (insn); ! 2659: ! 2660: /* Call insns which are *not* indirect and have unfilled delay slots. */ ! 2661: if (GET_CODE (insn) == CALL_INSN) ! 2662: { ! 2663: ! 2664: if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL ! 2665: && GET_CODE (XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)) == SYMBOL_REF) ! 2666: return 4; ! 2667: else if (GET_CODE (XVECEXP (pat, 0, 0)) == SET ! 2668: && GET_CODE (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)) ! 2669: == SYMBOL_REF) ! 2670: return 4; ! 2671: else ! 2672: return 0; ! 2673: } ! 2674: /* Millicode insn with an unfilled delay slot. */ ! 2675: else if (GET_CODE (insn) == INSN ! 2676: && GET_CODE (pat) != SEQUENCE ! 2677: && GET_CODE (pat) != USE ! 2678: && GET_CODE (pat) != CLOBBER ! 2679: && get_attr_type (insn) == TYPE_MILLI) ! 2680: return 4; ! 2681: /* Block move pattern. */ ! 2682: else if (GET_CODE (insn) == INSN ! 2683: && GET_CODE (pat) == PARALLEL ! 2684: && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 0)) == MEM ! 2685: && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 1)) == MEM ! 2686: && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode ! 2687: && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 1)) == BLKmode) ! 2688: return compute_movstrsi_length (insn) - 4; ! 2689: /* Conditional branch with an unfilled delay slot. */ ! 2690: else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn)) ! 2691: { ! 2692: /* Adjust a short backwards conditional with an unfilled delay slot. */ ! 2693: if (GET_CODE (pat) == SET ! 2694: && length == 4 ! 2695: && ! forward_branch_p (insn)) ! 2696: return 4; ! 2697: /* Adjust dbra insn with short backwards conditional branch with ! 2698: unfilled delay slot -- only for case where counter is in a ! 2699: general register register. */ ! 2700: else if (GET_CODE (pat) == PARALLEL ! 2701: && GET_CODE (XVECEXP (pat, 0, 1)) == SET ! 2702: && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == REG ! 2703: && ! FP_REG_P (XEXP (XVECEXP (pat, 0, 1), 0)) ! 2704: && length == 4 ! 2705: && ! forward_branch_p (insn)) ! 2706: return 4; ! 2707: else ! 2708: return 0; ! 2709: } ! 2710: else ! 2711: return 0; ! 2712: } ! 2713: ! 2714: /* Print operand X (an rtx) in assembler syntax to file FILE. ! 2715: CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. ! 2716: For `%' followed by punctuation, CODE is the punctuation and X is null. */ ! 2717: ! 2718: void ! 2719: print_operand (file, x, code) ! 2720: FILE *file; ! 2721: rtx x; ! 2722: int code; ! 2723: { ! 2724: switch (code) ! 2725: { ! 2726: case '\'': ! 2727: fputc (PA_QUOTE, file); ! 2728: return; ! 2729: break; ! 2730: ! 2731: case '#': ! 2732: /* Output a 'nop' if there's nothing for the delay slot. */ ! 2733: if (dbr_sequence_length () == 0) ! 2734: fputs ("\n\tnop", file); ! 2735: return; ! 2736: case '*': ! 2737: /* Output an nullification completer if there's nothing for the */ ! 2738: /* delay slot or nullification is requested. */ ! 2739: if (dbr_sequence_length () == 0 || ! 2740: (final_sequence && ! 2741: INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))) ! 2742: fputs (",n", file); ! 2743: return; ! 2744: case 'R': ! 2745: /* Print out the second register name of a register pair. ! 2746: I.e., R (6) => 7. */ ! 2747: fputs (reg_names[REGNO (x)+1], file); ! 2748: return; ! 2749: case 'r': ! 2750: /* A register or zero. */ ! 2751: if (x == const0_rtx ! 2752: || (x == CONST0_RTX (DFmode)) ! 2753: || (x == CONST0_RTX (SFmode))) ! 2754: { ! 2755: fputs ("0", file); ! 2756: return; ! 2757: } ! 2758: else ! 2759: break; ! 2760: case 'C': /* Plain (C)ondition */ ! 2761: case 'X': ! 2762: switch (GET_CODE (x)) ! 2763: { ! 2764: case EQ: ! 2765: fprintf (file, "="); break; ! 2766: case NE: ! 2767: fprintf (file, "<>"); break; ! 2768: case GT: ! 2769: fprintf (file, ">"); break; ! 2770: case GE: ! 2771: fprintf (file, ">="); break; ! 2772: case GEU: ! 2773: fprintf (file, ">>="); break; ! 2774: case GTU: ! 2775: fprintf (file, ">>"); break; ! 2776: case LT: ! 2777: fprintf (file, "<"); break; ! 2778: case LE: ! 2779: fprintf (file, "<="); break; ! 2780: case LEU: ! 2781: fprintf (file, "<<="); break; ! 2782: case LTU: ! 2783: fprintf (file, "<<"); break; ! 2784: default: ! 2785: printf ("Can't grok '%c' operator:\n", code); ! 2786: debug_rtx (x); ! 2787: abort (); ! 2788: } ! 2789: return; ! 2790: case 'N': /* Condition, (N)egated */ ! 2791: switch (GET_CODE (x)) ! 2792: { ! 2793: case EQ: ! 2794: fprintf (file, "<>"); break; ! 2795: case NE: ! 2796: fprintf (file, "="); break; ! 2797: case GT: ! 2798: fprintf (file, "<="); break; ! 2799: case GE: ! 2800: fprintf (file, "<"); break; ! 2801: case GEU: ! 2802: fprintf (file, "<<"); break; ! 2803: case GTU: ! 2804: fprintf (file, "<<="); break; ! 2805: case LT: ! 2806: fprintf (file, ">="); break; ! 2807: case LE: ! 2808: fprintf (file, ">"); break; ! 2809: case LEU: ! 2810: fprintf (file, ">>"); break; ! 2811: case LTU: ! 2812: fprintf (file, ">>="); break; ! 2813: default: ! 2814: printf ("Can't grok '%c' operator:\n", code); ! 2815: debug_rtx (x); ! 2816: abort (); ! 2817: } ! 2818: return; ! 2819: /* For floating point comparisons. Need special conditions to deal ! 2820: with NaNs properly. */ ! 2821: case 'Y': ! 2822: switch (GET_CODE (x)) ! 2823: { ! 2824: case EQ: ! 2825: fprintf (file, "!="); break; ! 2826: case NE: ! 2827: fprintf (file, "="); break; ! 2828: case GT: ! 2829: fprintf (file, "!>"); break; ! 2830: case GE: ! 2831: fprintf (file, "!>="); break; ! 2832: case LT: ! 2833: fprintf (file, "!<"); break; ! 2834: case LE: ! 2835: fprintf (file, "!<="); break; ! 2836: default: ! 2837: printf ("Can't grok '%c' operator:\n", code); ! 2838: debug_rtx (x); ! 2839: abort (); ! 2840: } ! 2841: return; ! 2842: case 'S': /* Condition, operands are (S)wapped. */ ! 2843: switch (GET_CODE (x)) ! 2844: { ! 2845: case EQ: ! 2846: fprintf (file, "="); break; ! 2847: case NE: ! 2848: fprintf (file, "<>"); break; ! 2849: case GT: ! 2850: fprintf (file, "<"); break; ! 2851: case GE: ! 2852: fprintf (file, "<="); break; ! 2853: case GEU: ! 2854: fprintf (file, "<<="); break; ! 2855: case GTU: ! 2856: fprintf (file, "<<"); break; ! 2857: case LT: ! 2858: fprintf (file, ">"); break; ! 2859: case LE: ! 2860: fprintf (file, ">="); break; ! 2861: case LEU: ! 2862: fprintf (file, ">>="); break; ! 2863: case LTU: ! 2864: fprintf (file, ">>"); break; ! 2865: default: ! 2866: printf ("Can't grok '%c' operator:\n", code); ! 2867: debug_rtx (x); ! 2868: abort (); ! 2869: } ! 2870: return; ! 2871: case 'B': /* Condition, (B)oth swapped and negate. */ ! 2872: switch (GET_CODE (x)) ! 2873: { ! 2874: case EQ: ! 2875: fprintf (file, "<>"); break; ! 2876: case NE: ! 2877: fprintf (file, "="); break; ! 2878: case GT: ! 2879: fprintf (file, ">="); break; ! 2880: case GE: ! 2881: fprintf (file, ">"); break; ! 2882: case GEU: ! 2883: fprintf (file, ">>"); break; ! 2884: case GTU: ! 2885: fprintf (file, ">>="); break; ! 2886: case LT: ! 2887: fprintf (file, "<="); break; ! 2888: case LE: ! 2889: fprintf (file, "<"); break; ! 2890: case LEU: ! 2891: fprintf (file, "<<"); break; ! 2892: case LTU: ! 2893: fprintf (file, "<<="); break; ! 2894: default: ! 2895: printf ("Can't grok '%c' operator:\n", code); ! 2896: debug_rtx (x); ! 2897: abort (); ! 2898: } ! 2899: return; ! 2900: case 'k': ! 2901: if (GET_CODE (x) == CONST_INT) ! 2902: { ! 2903: fprintf (file, "%d", ~INTVAL (x)); ! 2904: return; ! 2905: } ! 2906: abort(); ! 2907: case 'L': ! 2908: if (GET_CODE (x) == CONST_INT) ! 2909: { ! 2910: fprintf (file, "%d", 32 - (INTVAL (x) & 31)); ! 2911: return; ! 2912: } ! 2913: abort(); ! 2914: case 'O': ! 2915: if (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0) ! 2916: { ! 2917: fprintf (file, "%d", exact_log2 (INTVAL (x))); ! 2918: return; ! 2919: } ! 2920: abort(); ! 2921: case 'P': ! 2922: if (GET_CODE (x) == CONST_INT) ! 2923: { ! 2924: fprintf (file, "%d", 31 - (INTVAL (x) & 31)); ! 2925: return; ! 2926: } ! 2927: abort(); ! 2928: case 'I': ! 2929: if (GET_CODE (x) == CONST_INT) ! 2930: fputs ("i", file); ! 2931: return; ! 2932: case 'M': ! 2933: switch (GET_CODE (XEXP (x, 0))) ! 2934: { ! 2935: case PRE_DEC: ! 2936: case PRE_INC: ! 2937: fprintf (file, "s,mb"); ! 2938: break; ! 2939: case POST_DEC: ! 2940: case POST_INC: ! 2941: fprintf (file, "s,ma"); ! 2942: break; ! 2943: default: ! 2944: break; ! 2945: } ! 2946: return; ! 2947: case 'F': ! 2948: switch (GET_CODE (XEXP (x, 0))) ! 2949: { ! 2950: case PRE_DEC: ! 2951: case PRE_INC: ! 2952: fprintf (file, ",mb"); ! 2953: break; ! 2954: case POST_DEC: ! 2955: case POST_INC: ! 2956: fprintf (file, ",ma"); ! 2957: break; ! 2958: default: ! 2959: break; ! 2960: } ! 2961: return; ! 2962: case 'G': ! 2963: output_global_address (file, x); ! 2964: return; ! 2965: case 0: /* Don't do anything special */ ! 2966: break; ! 2967: case 'Z': ! 2968: { ! 2969: unsigned op[3]; ! 2970: compute_zdepi_operands (INTVAL (x), op); ! 2971: fprintf (file, "%d,%d,%d", op[0], op[1], op[2]); ! 2972: return; ! 2973: } ! 2974: default: ! 2975: abort (); ! 2976: } ! 2977: if (GET_CODE (x) == REG) ! 2978: fprintf (file, "%s", reg_names [REGNO (x)]); ! 2979: else if (GET_CODE (x) == MEM) ! 2980: { ! 2981: int size = GET_MODE_SIZE (GET_MODE (x)); ! 2982: rtx base = XEXP (XEXP (x, 0), 0); ! 2983: switch (GET_CODE (XEXP (x, 0))) ! 2984: { ! 2985: case PRE_DEC: ! 2986: case POST_DEC: ! 2987: fprintf (file, "-%d(0,%s)", size, reg_names [REGNO (base)]); ! 2988: break; ! 2989: case PRE_INC: ! 2990: case POST_INC: ! 2991: fprintf (file, "%d(0,%s)", size, reg_names [REGNO (base)]); ! 2992: break; ! 2993: default: ! 2994: output_address (XEXP (x, 0)); ! 2995: break; ! 2996: } ! 2997: } ! 2998: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) ! 2999: { ! 3000: union { double d; int i[2]; } u; ! 3001: union { float f; int i; } u1; ! 3002: u.i[0] = XINT (x, 0); u.i[1] = XINT (x, 1); ! 3003: u1.f = u.d; ! 3004: if (code == 'f') ! 3005: fprintf (file, "0r%.9g", u1.f); ! 3006: else ! 3007: fprintf (file, "0x%x", u1.i); ! 3008: } ! 3009: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) ! 3010: { ! 3011: union { double d; int i[2]; } u; ! 3012: u.i[0] = XINT (x, 0); u.i[1] = XINT (x, 1); ! 3013: fprintf (file, "0r%.20g", u.d); ! 3014: } ! 3015: else ! 3016: output_addr_const (file, x); ! 3017: } ! 3018: ! 3019: /* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF. */ ! 3020: ! 3021: void ! 3022: output_global_address (file, x) ! 3023: FILE *file; ! 3024: rtx x; ! 3025: { ! 3026: ! 3027: /* Imagine (high (const (plus ...))). */ ! 3028: if (GET_CODE (x) == HIGH) ! 3029: x = XEXP (x, 0); ! 3030: ! 3031: if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x)) ! 3032: assemble_name (file, XSTR (x, 0)); ! 3033: else if (GET_CODE (x) == SYMBOL_REF) ! 3034: { ! 3035: assemble_name (file, XSTR (x, 0)); ! 3036: #ifndef NeXT_ASM ! 3037: fprintf (file, "-$global$"); ! 3038: #endif ! 3039: } ! 3040: else if (GET_CODE (x) == CONST) ! 3041: { ! 3042: char *sep = ""; ! 3043: int offset = 0; /* assembler wants -$global$ at end */ ! 3044: rtx base; ! 3045: ! 3046: if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) ! 3047: { ! 3048: base = XEXP (XEXP (x, 0), 0); ! 3049: output_addr_const (file, base); ! 3050: } ! 3051: else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == CONST_INT) ! 3052: offset = INTVAL (XEXP (XEXP (x, 0), 0)); ! 3053: else abort (); ! 3054: ! 3055: #ifdef NeXT_ASM ! 3056: if (GET_CODE (XEXP (x, 0)) == MINUS) ! 3057: fprintf (file, "-"); ! 3058: #endif ! 3059: ! 3060: if (GET_CODE (XEXP (XEXP (x, 0), 1)) == SYMBOL_REF) ! 3061: { ! 3062: base = XEXP (XEXP (x, 0), 1); ! 3063: output_addr_const (file, base); ! 3064: } ! 3065: else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) ! 3066: offset = INTVAL (XEXP (XEXP (x, 0),1)); ! 3067: else abort (); ! 3068: ! 3069: if (GET_CODE (XEXP (x, 0)) == PLUS) ! 3070: { ! 3071: if (offset < 0) ! 3072: { ! 3073: offset = -offset; ! 3074: sep = "-"; ! 3075: } ! 3076: else ! 3077: sep = "+"; ! 3078: } ! 3079: else if (GET_CODE (XEXP (x, 0)) == MINUS ! 3080: && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)) ! 3081: sep = "-"; ! 3082: else abort (); ! 3083: ! 3084: #ifndef NeXT_ASM ! 3085: if (!read_only_operand (base)) ! 3086: fprintf (file, "-$global$"); ! 3087: #endif ! 3088: if (offset) ! 3089: { ! 3090: fprintf (file,"%s%d", sep, offset); ! 3091: } ! 3092: } ! 3093: else ! 3094: output_addr_const (file, x); ! 3095: } ! 3096: ! 3097: /* HP's millicode routines mean something special to the assembler. ! 3098: Keep track of which ones we have used. */ ! 3099: ! 3100: enum millicodes { remI, remU, divI, divU, mulI, mulU, end1000 }; ! 3101: static char imported[(int)end1000]; ! 3102: static char *milli_names[] = {"remI", "remU", "divI", "divU", "mulI", "mulU"}; ! 3103: static char import_string[] = ".IMPORT $$....,MILLICODE"; ! 3104: #define MILLI_START 10 ! 3105: ! 3106: static void ! 3107: import_milli (code) ! 3108: enum millicodes code; ! 3109: { ! 3110: #ifndef NeXT_ASM ! 3111: char str[sizeof (import_string)]; ! 3112: ! 3113: if (!imported[(int)code]) ! 3114: { ! 3115: imported[(int)code] = 1; ! 3116: strcpy (str, import_string); ! 3117: strncpy (str + MILLI_START, milli_names[(int)code], 4); ! 3118: output_asm_insn (str, 0); ! 3119: } ! 3120: #endif ! 3121: } ! 3122: ! 3123: /* The register constraints have put the operands and return value in ! 3124: the proper registers. */ ! 3125: ! 3126: char * ! 3127: output_mul_insn (unsignedp, insn) ! 3128: int unsignedp; ! 3129: rtx insn; ! 3130: { ! 3131: ! 3132: if (unsignedp) ! 3133: { ! 3134: import_milli (mulU); ! 3135: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulU"), ! 3136: gen_rtx (REG, SImode, 31)); ! 3137: } ! 3138: else ! 3139: { ! 3140: import_milli (mulI); ! 3141: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulI"), ! 3142: gen_rtx (REG, SImode, 31)); ! 3143: } ! 3144: } ! 3145: ! 3146: /* If operands isn't NULL, then it's a CONST_INT with which we can do ! 3147: something */ ! 3148: ! 3149: ! 3150: /* Emit the rtl for doing a division by a constant. */ ! 3151: ! 3152: /* Do magic division millicodes exist for this value? */ ! 3153: ! 3154: static int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, ! 3155: 1, 1}; ! 3156: ! 3157: /* We'll use an array to keep track of the magic millicodes and ! 3158: whether or not we've used them already. [n][0] is signed, [n][1] is ! 3159: unsigned. */ ! 3160: ! 3161: static int div_milli[16][2]; ! 3162: ! 3163: int ! 3164: div_operand (op, mode) ! 3165: rtx op; ! 3166: enum machine_mode mode; ! 3167: { ! 3168: return (mode == SImode ! 3169: && ((GET_CODE (op) == REG && REGNO (op) == 25) ! 3170: || (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 ! 3171: && INTVAL (op) < 16 && magic_milli[INTVAL (op)]))); ! 3172: } ! 3173: ! 3174: int ! 3175: emit_hpdiv_const (operands, unsignedp) ! 3176: rtx *operands; ! 3177: int unsignedp; ! 3178: { ! 3179: if (GET_CODE (operands[2]) == CONST_INT ! 3180: && INTVAL (operands[2]) > 0 ! 3181: && INTVAL (operands[2]) < 16 ! 3182: && magic_milli[INTVAL (operands[2])]) ! 3183: { ! 3184: emit_move_insn ( gen_rtx (REG, SImode, 26), operands[1]); ! 3185: emit ! 3186: (gen_rtx ! 3187: (PARALLEL, VOIDmode, ! 3188: gen_rtvec (6, gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 29), ! 3189: gen_rtx (unsignedp ? UDIV : DIV, SImode, ! 3190: gen_rtx (REG, SImode, 26), ! 3191: operands[2])), ! 3192: gen_rtx (CLOBBER, VOIDmode, operands[3]), ! 3193: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 26)), ! 3194: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 25)), ! 3195: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 19)), ! 3196: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 31))))); ! 3197: emit_move_insn (operands[0], gen_rtx (REG, SImode, 29)); ! 3198: return 1; ! 3199: } ! 3200: return 0; ! 3201: } ! 3202: ! 3203: char * ! 3204: output_div_insn (operands, unsignedp, insn) ! 3205: rtx *operands; ! 3206: int unsignedp; ! 3207: rtx insn; ! 3208: { ! 3209: int divisor; ! 3210: ! 3211: /* If the divisor is a constant, try to use one of the special ! 3212: opcodes .*/ ! 3213: if (GET_CODE (operands[0]) == CONST_INT) ! 3214: { ! 3215: static char buf[100]; ! 3216: divisor = INTVAL (operands[0]); ! 3217: if (!div_milli[divisor][unsignedp]) ! 3218: { ! 3219: div_milli[divisor][unsignedp] = 1; ! 3220: #ifndef NeXT_ASM ! 3221: if (unsignedp) ! 3222: output_asm_insn (".IMPORT $$divU_%0,MILLICODE", operands); ! 3223: else ! 3224: output_asm_insn (".IMPORT $$divI_%0,MILLICODE", operands); ! 3225: #endif ! 3226: } ! 3227: if (unsignedp) ! 3228: { ! 3229: sprintf (buf, "$$divU_%d", INTVAL (operands[0])); ! 3230: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, buf), ! 3231: gen_rtx (REG, SImode, 31)); ! 3232: } ! 3233: else ! 3234: { ! 3235: sprintf (buf, "$$divI_%d", INTVAL (operands[0])); ! 3236: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, buf), ! 3237: gen_rtx (REG, SImode, 31)); ! 3238: } ! 3239: } ! 3240: /* Divisor isn't a special constant. */ ! 3241: else ! 3242: { ! 3243: if (unsignedp) ! 3244: { ! 3245: import_milli (divU); ! 3246: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$divU"), ! 3247: gen_rtx (REG, SImode, 31)); ! 3248: } ! 3249: else ! 3250: { ! 3251: import_milli (divI); ! 3252: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$divI"), ! 3253: gen_rtx (REG, SImode, 31)); ! 3254: } ! 3255: } ! 3256: } ! 3257: ! 3258: /* Output a $$rem millicode to do mod. */ ! 3259: ! 3260: char * ! 3261: output_mod_insn (unsignedp, insn) ! 3262: int unsignedp; ! 3263: rtx insn; ! 3264: { ! 3265: if (unsignedp) ! 3266: { ! 3267: import_milli (remU); ! 3268: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$remU"), ! 3269: gen_rtx (REG, SImode, 31)); ! 3270: } ! 3271: else ! 3272: { ! 3273: import_milli (remI); ! 3274: return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$remI"), ! 3275: gen_rtx (REG, SImode, 31)); ! 3276: } ! 3277: } ! 3278: ! 3279: void ! 3280: output_arg_descriptor (insn) ! 3281: rtx insn; ! 3282: { ! 3283: char *arg_regs[4]; ! 3284: enum machine_mode arg_mode; ! 3285: rtx prev_insn; ! 3286: int i, output_flag = 0; ! 3287: int regno; ! 3288: ! 3289: for (i = 0; i < 4; i++) ! 3290: arg_regs[i] = 0; ! 3291: ! 3292: for (prev_insn = PREV_INSN (insn); GET_CODE (prev_insn) == INSN; ! 3293: prev_insn = PREV_INSN (prev_insn)) ! 3294: { ! 3295: /* Terminate search for arguments if a non-USE insn is encountered ! 3296: or a USE insn which does not specify an argument, STATIC_CHAIN, ! 3297: or STRUCT_VALUE register. */ ! 3298: if (!(GET_CODE (PATTERN (prev_insn)) == USE ! 3299: && GET_CODE (XEXP (PATTERN (prev_insn), 0)) == REG ! 3300: && (FUNCTION_ARG_REGNO_P (REGNO (XEXP (PATTERN (prev_insn), 0))) ! 3301: || REGNO (XEXP (PATTERN (prev_insn), 0)) == STATIC_CHAIN_REGNUM ! 3302: || REGNO (XEXP (PATTERN (prev_insn), 0)) ! 3303: == STRUCT_VALUE_REGNUM))) ! 3304: break; ! 3305: ! 3306: /* If this is a USE for the STATIC_CHAIN or STRUCT_VALUE register, ! 3307: then skip it and continue the loop since those are not encoded ! 3308: in the argument relocation bits. */ ! 3309: if (REGNO (XEXP (PATTERN (prev_insn), 0)) == STATIC_CHAIN_REGNUM ! 3310: || REGNO (XEXP (PATTERN (prev_insn), 0)) == STRUCT_VALUE_REGNUM) ! 3311: continue; ! 3312: ! 3313: arg_mode = GET_MODE (XEXP (PATTERN (prev_insn), 0)); ! 3314: regno = REGNO (XEXP (PATTERN (prev_insn), 0)); ! 3315: if (regno >= 23 && regno <= 26) ! 3316: { ! 3317: arg_regs[26 - regno] = "GR"; ! 3318: if (arg_mode == DImode) ! 3319: arg_regs[25 - regno] = "GR"; ! 3320: } ! 3321: else if (!TARGET_SNAKE) /* fp args */ ! 3322: { ! 3323: if (arg_mode == SFmode) ! 3324: arg_regs[regno - 32] = "FR"; ! 3325: else ! 3326: { ! 3327: #ifndef HP_FP_ARG_DESCRIPTOR_REVERSED ! 3328: arg_regs[regno - 33] = "FR"; ! 3329: arg_regs[regno - 32] = "FU"; ! 3330: #else ! 3331: arg_regs[regno - 33] = "FU"; ! 3332: arg_regs[regno - 32] = "FR"; ! 3333: #endif ! 3334: } ! 3335: } ! 3336: else ! 3337: { ! 3338: if (arg_mode == SFmode) ! 3339: arg_regs[(regno - 44) / 2] = "FR"; ! 3340: else ! 3341: { ! 3342: #ifndef HP_FP_ARG_DESCRIPTOR_REVERSED ! 3343: arg_regs[(regno - 46) / 2] = "FR"; ! 3344: arg_regs[(regno - 46) / 2 + 1] = "FU"; ! 3345: #else ! 3346: arg_regs[(regno - 46) / 2] = "FU"; ! 3347: arg_regs[(regno - 46) / 2 + 1] = "FR"; ! 3348: #endif ! 3349: } ! 3350: } ! 3351: } ! 3352: #ifndef NeXT_ASM ! 3353: fputs ("\t.CALL ", asm_out_file); ! 3354: for (i = 0; i < 4; i++) ! 3355: { ! 3356: if (arg_regs[i]) ! 3357: { ! 3358: if (output_flag++) ! 3359: fputc (',', asm_out_file); ! 3360: fprintf (asm_out_file, "ARGW%d=%s", i, arg_regs[i]); ! 3361: } ! 3362: } ! 3363: fputc ('\n', asm_out_file); ! 3364: #endif ! 3365: } ! 3366: ! 3367: /* Memory loads/stores to/from the shift need to go through ! 3368: the general registers. */ ! 3369: ! 3370: enum reg_class ! 3371: secondary_reload_class (class, mode, in) ! 3372: enum reg_class class; ! 3373: enum machine_mode mode; ! 3374: rtx in; ! 3375: { ! 3376: int regno = true_regnum (in); ! 3377: ! 3378: if (function_label_operand (in, mode) ! 3379: || ((regno >= FIRST_PSEUDO_REGISTER || regno == -1) ! 3380: && GET_MODE_CLASS (mode) == MODE_INT ! 3381: && FP_REG_CLASS_P (class)) ! 3382: || (class == SHIFT_REGS && (regno <= 0 || regno >= 32))) ! 3383: return GENERAL_REGS; ! 3384: ! 3385: if (GET_CODE (in) == HIGH) ! 3386: in = XEXP (in, 0); ! 3387: ! 3388: if (class != R1_REGS && symbolic_operand (in, VOIDmode)) ! 3389: return R1_REGS; ! 3390: ! 3391: return NO_REGS; ! 3392: } ! 3393: ! 3394: enum direction ! 3395: function_arg_padding (mode, type) ! 3396: enum machine_mode mode; ! 3397: tree type; ! 3398: { ! 3399: int size; ! 3400: ! 3401: if (mode == BLKmode) ! 3402: { ! 3403: if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) ! 3404: size = int_size_in_bytes (type) * BITS_PER_UNIT; ! 3405: else ! 3406: return upward; /* Don't know if this is right, but */ ! 3407: /* same as old definition. */ ! 3408: } ! 3409: else ! 3410: size = GET_MODE_BITSIZE (mode); ! 3411: if (size < PARM_BOUNDARY) ! 3412: return downward; ! 3413: else if (size % PARM_BOUNDARY) ! 3414: return upward; ! 3415: else ! 3416: return none; ! 3417: } ! 3418: ! 3419: ! 3420: /* Do what is necessary for `va_start'. The argument is ignored; ! 3421: We look at the current function to determine if stdargs or varargs ! 3422: is used and fill in an initial va_list. A pointer to this constructor ! 3423: is returned. */ ! 3424: ! 3425: struct rtx_def * ! 3426: hppa_builtin_saveregs (arglist) ! 3427: tree arglist; ! 3428: { ! 3429: rtx offset; ! 3430: tree fntype = TREE_TYPE (current_function_decl); ! 3431: int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 ! 3432: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) ! 3433: != void_type_node))) ! 3434: ? UNITS_PER_WORD : 0); ! 3435: ! 3436: if (argadj) ! 3437: offset = plus_constant (current_function_arg_offset_rtx, argadj); ! 3438: else ! 3439: offset = current_function_arg_offset_rtx; ! 3440: ! 3441: /* Store general registers on the stack. */ ! 3442: move_block_from_reg (23, ! 3443: gen_rtx (MEM, BLKmode, ! 3444: plus_constant ! 3445: (current_function_internal_arg_pointer, -16)), ! 3446: 4, 4 * UNITS_PER_WORD); ! 3447: return copy_to_reg (expand_binop (Pmode, add_optab, ! 3448: current_function_internal_arg_pointer, ! 3449: offset, 0, 0, OPTAB_LIB_WIDEN)); ! 3450: } ! 3451: ! 3452: /* This routine handles all the normal conditional branch sequences we ! 3453: might need to generate. It handles compare immediate vs compare ! 3454: register, nullification of delay slots, varying length branches, ! 3455: negated branches, and all combinations of the above. It returns the ! 3456: output appropriate to emit the branch corresponding to all given ! 3457: parameters. */ ! 3458: ! 3459: char * ! 3460: output_cbranch (operands, nullify, length, negated, insn) ! 3461: rtx *operands; ! 3462: int nullify, length, negated; ! 3463: rtx insn; ! 3464: { ! 3465: static char buf[100]; ! 3466: int useskip = 0; ! 3467: ! 3468: /* A conditional branch to the following instruction (eg the delay slot) is ! 3469: asking for a disaster. This can happen when not optimizing. ! 3470: ! 3471: In such cases it is safe to emit nothing. */ ! 3472: ! 3473: if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) ! 3474: return ""; ! 3475: ! 3476: /* If this is a long branch with its delay slot unfilled, set `nullify' ! 3477: as it can nullify the delay slot and save a nop. */ ! 3478: if (length == 8 && dbr_sequence_length () == 0) ! 3479: nullify = 1; ! 3480: ! 3481: /* If this is a short forward conditional branch which did not get ! 3482: its delay slot filled, the delay slot can still be nullified. */ ! 3483: if (! nullify && length == 4 && dbr_sequence_length () == 0) ! 3484: nullify = forward_branch_p (insn); ! 3485: ! 3486: /* A forward branch over a single nullified insn can be done with a ! 3487: comclr instruction. This avoids a single cycle penalty due to ! 3488: mis-predicted branch if we fall through (branch not taken). */ ! 3489: if (length == 4 ! 3490: && next_real_insn (insn) != 0 ! 3491: && get_attr_length (next_real_insn (insn)) == 4 ! 3492: && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) ! 3493: && nullify) ! 3494: useskip = 1; ! 3495: ! 3496: switch (length) ! 3497: { ! 3498: /* All short conditional branches except backwards with an unfilled ! 3499: delay slot. */ ! 3500: case 4: ! 3501: if (useskip) ! 3502: strcpy (buf, "com%I2clr,"); ! 3503: else ! 3504: strcpy (buf, "com%I2b,"); ! 3505: if (negated) ! 3506: strcat (buf, "%B3"); ! 3507: else ! 3508: strcat (buf, "%S3"); ! 3509: if (useskip) ! 3510: strcat (buf, " %2,%1,%%r0"); ! 3511: else if (nullify) ! 3512: strcat (buf, ",n %2,%1,%0"); ! 3513: else ! 3514: strcat (buf, " %2,%1,%0"); ! 3515: break; ! 3516: ! 3517: /* All long conditionals. Note an short backward branch with an ! 3518: unfilled delay slot is treated just like a long backward branch ! 3519: with an unfilled delay slot. */ ! 3520: case 8: ! 3521: /* Handle weird backwards branch with a filled delay slot ! 3522: with is nullified. */ ! 3523: if (dbr_sequence_length () != 0 ! 3524: && ! forward_branch_p (insn) ! 3525: && nullify) ! 3526: { ! 3527: strcpy (buf, "com%I2b,"); ! 3528: if (negated) ! 3529: strcat (buf, "%S3"); ! 3530: else ! 3531: strcat (buf, "%B3"); ! 3532: strcat (buf, ",n %2,%1,.+12\n\tbl %0,%%r0"); ! 3533: } ! 3534: else ! 3535: { ! 3536: strcpy (buf, "com%I2clr,"); ! 3537: if (negated) ! 3538: strcat (buf, "%S3"); ! 3539: else ! 3540: strcat (buf, "%B3"); ! 3541: if (nullify) ! 3542: strcat (buf, " %2,%1,%%r0\n\tbl,n %0,%%r0"); ! 3543: else ! 3544: strcat (buf, " %2,%1,%%r0\n\tbl %0,%%r0"); ! 3545: } ! 3546: break; ! 3547: ! 3548: default: ! 3549: abort(); ! 3550: } ! 3551: return buf; ! 3552: } ! 3553: ! 3554: /* This routine handles all the branch-on-bit conditional branch sequences we ! 3555: might need to generate. It handles nullification of delay slots, ! 3556: varying length branches, negated branches and all combinations of the ! 3557: above. it returns the appropriate output template to emit the branch. */ ! 3558: ! 3559: char * ! 3560: output_bb (operands, nullify, length, negated, insn, which) ! 3561: rtx *operands; ! 3562: int nullify, length, negated; ! 3563: rtx insn; ! 3564: int which; ! 3565: { ! 3566: static char buf[100]; ! 3567: int useskip = 0; ! 3568: ! 3569: /* A conditional branch to the following instruction (eg the delay slot) is ! 3570: asking for a disaster. I do not think this can happen as this pattern ! 3571: is only used when optimizing; jump optimization should eliminate the ! 3572: jump. But be prepared just in case. */ ! 3573: ! 3574: if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) ! 3575: return ""; ! 3576: ! 3577: /* If this is a long branch with its delay slot unfilled, set `nullify' ! 3578: as it can nullify the delay slot and save a nop. */ ! 3579: if (length == 8 && dbr_sequence_length () == 0) ! 3580: nullify = 1; ! 3581: ! 3582: /* If this is a short forward conditional branch which did not get ! 3583: its delay slot filled, the delay slot can still be nullified. */ ! 3584: if (! nullify && length == 4 && dbr_sequence_length () == 0) ! 3585: nullify = forward_branch_p (insn); ! 3586: ! 3587: /* A forward branch over a single nullified insn can be done with a ! 3588: extrs instruction. This avoids a single cycle penalty due to ! 3589: mis-predicted branch if we fall through (branch not taken). */ ! 3590: ! 3591: if (length == 4 ! 3592: && next_real_insn (insn) != 0 ! 3593: && get_attr_length (next_real_insn (insn)) == 4 ! 3594: && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) ! 3595: && nullify) ! 3596: useskip = 1; ! 3597: ! 3598: switch (length) ! 3599: { ! 3600: ! 3601: /* All short conditional branches except backwards with an unfilled ! 3602: delay slot. */ ! 3603: case 4: ! 3604: if (useskip) ! 3605: strcpy (buf, "extrs,"); ! 3606: else ! 3607: strcpy (buf, "bb,"); ! 3608: if ((which == 0 && negated) ! 3609: || (which == 1 && ! negated)) ! 3610: strcat (buf, ">="); ! 3611: else ! 3612: strcat (buf, "<"); ! 3613: if (useskip) ! 3614: strcat (buf, " %0,%1,1,0"); ! 3615: else if (nullify && negated) ! 3616: strcat (buf, ",n %0,%1,%3"); ! 3617: else if (nullify && ! negated) ! 3618: strcat (buf, ",n %0,%1,%2"); ! 3619: else if (! nullify && negated) ! 3620: strcat (buf, "%0,%1,%3"); ! 3621: else if (! nullify && ! negated) ! 3622: strcat (buf, " %0,%1,%2"); ! 3623: break; ! 3624: ! 3625: /* All long conditionals. Note an short backward branch with an ! 3626: unfilled delay slot is treated just like a long backward branch ! 3627: with an unfilled delay slot. */ ! 3628: case 8: ! 3629: /* Handle weird backwards branch with a filled delay slot ! 3630: with is nullified. */ ! 3631: if (dbr_sequence_length () != 0 ! 3632: && ! forward_branch_p (insn) ! 3633: && nullify) ! 3634: { ! 3635: strcpy (buf, "bb,"); ! 3636: if ((which == 0 && negated) ! 3637: || (which == 1 && ! negated)) ! 3638: strcat (buf, "<"); ! 3639: else ! 3640: strcat (buf, ">="); ! 3641: if (negated) ! 3642: strcat (buf, " %0,%1,.+12\n\tbl %3,%%r0"); ! 3643: else ! 3644: strcat (buf, " %0,%1,.+12\n\tbl %2,%%r0"); ! 3645: } ! 3646: else ! 3647: { ! 3648: strcpy (buf, "extrs,"); ! 3649: if ((which == 0 && negated) ! 3650: || (which == 1 && ! negated)) ! 3651: strcat (buf, "<"); ! 3652: else ! 3653: strcat (buf, ">="); ! 3654: if (nullify && negated) ! 3655: strcat (buf, " %0,%1,1,0\n\tbl,n %3,%%r0"); ! 3656: else if (nullify && ! negated) ! 3657: strcat (buf, " %0,%1,1,0\n\tbl,n %2,%%r0"); ! 3658: else if (negated) ! 3659: strcat (buf, " %0,%1,1,0\n\tbl %3,%%r0"); ! 3660: else ! 3661: strcat (buf, " %0,%1,1,0\n\tbl %2,%%r0"); ! 3662: } ! 3663: break; ! 3664: ! 3665: default: ! 3666: abort(); ! 3667: } ! 3668: return buf; ! 3669: } ! 3670: ! 3671: /* Return the output template for emitting a dbra type insn. ! 3672: ! 3673: Note it may perform some output operations on its own before ! 3674: returning the final output string. */ ! 3675: char * ! 3676: output_dbra (operands, insn, which_alternative) ! 3677: rtx *operands; ! 3678: rtx insn; ! 3679: int which_alternative; ! 3680: { ! 3681: ! 3682: /* A conditional branch to the following instruction (eg the delay slot) is ! 3683: asking for a disaster. Be prepared! */ ! 3684: ! 3685: if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) ! 3686: { ! 3687: if (which_alternative == 0) ! 3688: return "ldo %1(%0),%0"; ! 3689: else if (which_alternative == 1) ! 3690: { ! 3691: output_asm_insn ("fstws %0,-16(0,%%r30)",operands); ! 3692: output_asm_insn ("ldw -16(0,%%r30),%4",operands); ! 3693: output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); ! 3694: return "fldws -16(0,%%r30),%0"; ! 3695: } ! 3696: else ! 3697: { ! 3698: output_asm_insn ("ldw %0,%4", operands); ! 3699: return "ldo %1(%4),%4\n\tstw %4,%0"; ! 3700: } ! 3701: } ! 3702: ! 3703: if (which_alternative == 0) ! 3704: { ! 3705: int nullify = INSN_ANNULLED_BRANCH_P (insn); ! 3706: int length = get_attr_length (insn); ! 3707: ! 3708: /* If this is a long branch with its delay slot unfilled, set `nullify' ! 3709: as it can nullify the delay slot and save a nop. */ ! 3710: if (length == 8 && dbr_sequence_length () == 0) ! 3711: nullify = 1; ! 3712: ! 3713: /* If this is a short forward conditional branch which did not get ! 3714: its delay slot filled, the delay slot can still be nullified. */ ! 3715: if (! nullify && length == 4 && dbr_sequence_length () == 0) ! 3716: nullify = forward_branch_p (insn); ! 3717: ! 3718: /* Handle short versions first. */ ! 3719: if (length == 4 && nullify) ! 3720: return "addib,%C2,n %1,%0,%3"; ! 3721: else if (length == 4 && ! nullify) ! 3722: return "addib,%C2 %1,%0,%3"; ! 3723: else if (length == 8) ! 3724: { ! 3725: /* Handle weird backwards branch with a fulled delay slot ! 3726: which is nullified. */ ! 3727: if (dbr_sequence_length () != 0 ! 3728: && ! forward_branch_p (insn) ! 3729: && nullify) ! 3730: return "addib,%N2,n %1,%0,.+12\n\tbl %3,0"; ! 3731: ! 3732: /* Handle normal cases. */ ! 3733: if (nullify) ! 3734: return "addi,%N2 %1,%0,%0\n\tbl,n %3,0"; ! 3735: else ! 3736: return "addi,%N2 %1,%0,%0\n\tbl %3,0"; ! 3737: } ! 3738: else ! 3739: abort(); ! 3740: } ! 3741: /* Deal with gross reload from FP register case. */ ! 3742: else if (which_alternative == 1) ! 3743: { ! 3744: /* Move loop counter from FP register to MEM then into a GR, ! 3745: increment the GR, store the GR into MEM, and finally reload ! 3746: the FP register from MEM from within the branch's delay slot. */ ! 3747: output_asm_insn ("fstws %0,-16(0,%%r30)\n\tldw -16(0,%%r30),%4",operands); ! 3748: output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); ! 3749: if (get_attr_length (insn) == 24) ! 3750: return "comb,%S2 0,%4,%3\n\tfldws -16(0,%%r30),%0"; ! 3751: else ! 3752: return "comclr,%B2 0,%4,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; ! 3753: } ! 3754: /* Deal with gross reload from memory case. */ ! 3755: else ! 3756: { ! 3757: /* Reload loop counter from memory, the store back to memory ! 3758: happens in the branch's delay slot. */ ! 3759: output_asm_insn ("ldw %0,%4", operands); ! 3760: if (get_attr_length (insn) == 12) ! 3761: return "addib,%C2 %1,%4,%3\n\tstw %4,%0"; ! 3762: else ! 3763: return "addi,%N2 %1,%4,%4\n\tbl %3,0\n\tstw %4,%0"; ! 3764: } ! 3765: } ! 3766: ! 3767: /* Return the output template for emitting a dbra type insn. ! 3768: ! 3769: Note it may perform some output operations on its own before ! 3770: returning the final output string. */ ! 3771: char * ! 3772: output_movb (operands, insn, which_alternative, reverse_comparison) ! 3773: rtx *operands; ! 3774: rtx insn; ! 3775: int which_alternative; ! 3776: int reverse_comparison; ! 3777: { ! 3778: ! 3779: /* A conditional branch to the following instruction (eg the delay slot) is ! 3780: asking for a disaster. Be prepared! */ ! 3781: ! 3782: if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) ! 3783: { ! 3784: if (which_alternative == 0) ! 3785: return "copy %1,%0"; ! 3786: else if (which_alternative == 1) ! 3787: { ! 3788: output_asm_insn ("stw %1,-16(0,%%r30)",operands); ! 3789: return "fldws -16(0,%%r30),%0"; ! 3790: } ! 3791: else ! 3792: return "stw %1,%0"; ! 3793: } ! 3794: ! 3795: /* Support the second variant. */ ! 3796: if (reverse_comparison) ! 3797: PUT_CODE (operands[2], reverse_condition (GET_CODE (operands[2]))); ! 3798: ! 3799: if (which_alternative == 0) ! 3800: { ! 3801: int nullify = INSN_ANNULLED_BRANCH_P (insn); ! 3802: int length = get_attr_length (insn); ! 3803: ! 3804: /* If this is a long branch with its delay slot unfilled, set `nullify' ! 3805: as it can nullify the delay slot and save a nop. */ ! 3806: if (length == 8 && dbr_sequence_length () == 0) ! 3807: nullify = 1; ! 3808: ! 3809: /* If this is a short forward conditional branch which did not get ! 3810: its delay slot filled, the delay slot can still be nullified. */ ! 3811: if (! nullify && length == 4 && dbr_sequence_length () == 0) ! 3812: nullify = forward_branch_p (insn); ! 3813: ! 3814: /* Handle short versions first. */ ! 3815: if (length == 4 && nullify) ! 3816: return "movb,%C2,n %1,%0,%3"; ! 3817: else if (length == 4 && ! nullify) ! 3818: return "movb,%C2 %1,%0,%3"; ! 3819: else if (length == 8) ! 3820: { ! 3821: /* Handle weird backwards branch with a fulled delay slot ! 3822: which is nullified. */ ! 3823: if (dbr_sequence_length () != 0 ! 3824: && ! forward_branch_p (insn) ! 3825: && nullify) ! 3826: return "movb,%N2,n %1,%0,.+12\n\ttbl %3,0"; ! 3827: ! 3828: /* Handle normal cases. */ ! 3829: if (nullify) ! 3830: return "or,%N2 %1,%%r0,%0\n\tbl,n %3,0"; ! 3831: else ! 3832: return "or,%N2 %1,%%r0,%0\n\tbl %3,0"; ! 3833: } ! 3834: else ! 3835: abort(); ! 3836: } ! 3837: /* Deal with gross reload from FP register case. */ ! 3838: else if (which_alternative == 1) ! 3839: { ! 3840: /* Move loop counter from FP register to MEM then into a GR, ! 3841: increment the GR, store the GR into MEM, and finally reload ! 3842: the FP register from MEM from within the branch's delay slot. */ ! 3843: output_asm_insn ("stw %1,-16(0,%%r30)",operands); ! 3844: if (get_attr_length (insn) == 12) ! 3845: return "comb,%S2 0,%1,%3\n\tfldws -16(0,%%r30),%0"; ! 3846: else ! 3847: return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; ! 3848: } ! 3849: /* Deal with gross reload from memory case. */ ! 3850: else ! 3851: { ! 3852: /* Reload loop counter from memory, the store back to memory ! 3853: happens in the branch's delay slot. */ ! 3854: if (get_attr_length (insn) == 8) ! 3855: return "comb,%S2 0,%1,%3\n\tstw %1,%0"; ! 3856: else ! 3857: return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tstw %1,%0"; ! 3858: } ! 3859: } ! 3860: ! 3861: ! 3862: /* Output a local call insn not emitting insns for the delay slot. ! 3863: If -mstub-calls is enabled, we emit the pseudo insn "jbsr", to ! 3864: a locally generated stub, and make that stub do the actual call. ! 3865: ! 3866: CALL_DEST is the routine we are calling. ! 3867: ! 3868: RETURN_POINTER is the register which will hold the return address. ! 3869: %r2 for most calls, %r31 for millicode calls. */ ! 3870: #ifdef NeXT_ASM ! 3871: void add_compiler_stub PROTO((tree, tree, int)); ! 3872: void output_compiler_stub PROTO ((void)); ! 3873: int no_previous_def PROTO((tree)); ! 3874: tree get_prev_label PROTO((tree)); ! 3875: ! 3876: #endif ! 3877: ! 3878: void ! 3879: output_call_insn (insn, call_dest, return_pointer) ! 3880: rtx insn; ! 3881: rtx call_dest; ! 3882: rtx return_pointer; ! 3883: ! 3884: { ! 3885: rtx xoperands[2]; ! 3886: ! 3887: xoperands[0] = call_dest; ! 3888: xoperands[1] = return_pointer; ! 3889: ! 3890: #ifdef NeXT_ASM ! 3891: if(GET_CODE (call_dest) == SYMBOL_REF) ! 3892: { ! 3893: rtx prev_insn, label_rtx; ! 3894: int line_number; ! 3895: static char buf[256]; ! 3896: static char temp_buf[256]; ! 3897: char *label_buf; ! 3898: tree labelname; ! 3899: tree funname = get_identifier (XSTR (call_dest, 0)); ! 3900: ! 3901: if (!flag_pic && !strncmp (IDENTIFIER_POINTER (funname), "$$", 2)) ! 3902: { ! 3903: { ! 3904: strcpy (temp_buf, "ldil L%'"); ! 3905: strcat (temp_buf, IDENTIFIER_POINTER (funname)); ! 3906: strcat (temp_buf, ",%%r31\n\tble R%'"); ! 3907: strcat (temp_buf, IDENTIFIER_POINTER (funname)); ! 3908: strcat (temp_buf, "(4,%%r31)"); ! 3909: output_asm_insn (temp_buf, 0); ! 3910: } ! 3911: return; ! 3912: } ! 3913: else if (!strncmp (IDENTIFIER_POINTER (funname), "$$", 2)) ! 3914: { ! 3915: strcpy (temp_buf, "*"); ! 3916: strcat (temp_buf, XSTR (call_dest, 0)); ! 3917: funname = get_identifier (temp_buf); ! 3918: } ! 3919: if (no_previous_def (funname)) ! 3920: { ! 3921: label_rtx = gen_label_rtx (); ! 3922: ! 3923: ASM_GENERATE_INTERNAL_LABEL (temp_buf, "L", CODE_LABEL_NUMBER(label_rtx)); ! 3924: ! 3925: if (temp_buf[0] == '*') ! 3926: label_buf = temp_buf + 1; ! 3927: else ! 3928: label_buf = temp_buf; ! 3929: ! 3930: while (insn && GET_CODE (insn) != NOTE) ! 3931: insn = PREV_INSN (insn); ! 3932: ! 3933: if (insn) ! 3934: line_number = NOTE_LINE_NUMBER (insn); ! 3935: ! 3936: labelname =get_identifier (label_buf); ! 3937: add_compiler_stub(labelname, funname, line_number); ! 3938: } ! 3939: else ! 3940: { ! 3941: labelname = get_prev_label (funname); ! 3942: } ! 3943: ! 3944: strcpy(buf, "jbsr %0,%r1,"); ! 3945: strcat(buf, IDENTIFIER_POINTER (labelname)); ! 3946: ! 3947: output_asm_insn (buf, xoperands); ! 3948: } ! 3949: else ! 3950: #endif ! 3951: output_asm_insn ("bl %0,%r1", xoperands); ! 3952: } ! 3953: ! 3954: /* INSN is either a function call or a millicode call. It may have an ! 3955: unconditional jump in its delay slot. ! 3956: ! 3957: CALL_DEST is the routine we are calling. ! 3958: ! 3959: RETURN_POINTER is the register which will hold the return address. ! 3960: %r2 for most calls, %r31 for millicode calls. */ ! 3961: char * ! 3962: output_call (insn, call_dest, return_pointer) ! 3963: rtx insn; ! 3964: rtx call_dest; ! 3965: rtx return_pointer; ! 3966: ! 3967: { ! 3968: int distance; ! 3969: rtx xoperands[4]; ! 3970: rtx seq_insn; ! 3971: ! 3972: /* Handle common case -- empty delay slot or no jump in the delay slot. */ ! 3973: if (dbr_sequence_length () == 0 ! 3974: || (dbr_sequence_length () != 0 ! 3975: && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)) ! 3976: { ! 3977: output_call_insn (insn, call_dest, return_pointer); ! 3978: if (dbr_sequence_length () == 0) ! 3979: output_asm_insn ("nop", xoperands); ! 3980: return ""; ! 3981: } ! 3982: ! 3983: /* This call has an unconditional jump in its delay slot. */ ! 3984: ! 3985: /* Use the containing sequence insn's address. */ ! 3986: seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); ! 3987: ! 3988: distance = insn_addresses[INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))] ! 3989: - insn_addresses[INSN_UID (seq_insn)] - 8; ! 3990: ! 3991: /* If the branch was too far away, emit a normal call followed ! 3992: by a nop, followed by the unconditional branch. ! 3993: ! 3994: If the branch is close, then adjust %r2 from within the ! 3995: call's delay slot. */ ! 3996: ! 3997: xoperands[0] = call_dest; ! 3998: xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1); ! 3999: xoperands[2] = return_pointer; ! 4000: if (! VAL_14_BITS_P (distance)) ! 4001: { ! 4002: output_call_insn (insn, call_dest, return_pointer); ! 4003: output_asm_insn ("nop\n\tbl,n %1,%%r0", xoperands); ! 4004: } ! 4005: else ! 4006: { ! 4007: xoperands[3] = gen_label_rtx (); ! 4008: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", ! 4009: CODE_LABEL_NUMBER (xoperands[3])); ! 4010: output_call_insn (insn, call_dest, return_pointer); ! 4011: output_asm_insn ("ldo %1-%3-8(%r2),%r2", xoperands); ! 4012: } ! 4013: ! 4014: /* Delete the jump. */ ! 4015: PUT_CODE (NEXT_INSN (insn), NOTE); ! 4016: NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; ! 4017: NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; ! 4018: return ""; ! 4019: } ! 4020: ! 4021: extern struct obstack *saveable_obstack; ! 4022: ! 4023: /* In HPUX 8.0's shared library scheme, special relocations are needed ! 4024: for function labels if they might be passed to a function ! 4025: in a shared library (because shared libraries don't live in code ! 4026: space), and special magic is needed to construct their address. */ ! 4027: ! 4028: void ! 4029: hppa_encode_label (sym) ! 4030: rtx sym; ! 4031: { ! 4032: char *str = XSTR (sym, 0); ! 4033: int len = strlen (str); ! 4034: char *newstr = obstack_alloc (saveable_obstack, len + 2) ; ! 4035: ! 4036: if (str[0] == '*') ! 4037: *newstr++ = *str++; ! 4038: strcpy (newstr + 1, str); ! 4039: *newstr = '@'; ! 4040: XSTR (sym,0) = newstr; ! 4041: } ! 4042: ! 4043: int ! 4044: function_label_operand (op, mode) ! 4045: rtx op; ! 4046: enum machine_mode mode; ! 4047: { ! 4048: return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0)); ! 4049: } ! 4050: ! 4051: /* Returns 1 if the 6 operands specified in OPERANDS are suitable for ! 4052: use in fmpyadd instructions. */ ! 4053: int ! 4054: fmpyaddoperands(operands) ! 4055: rtx *operands; ! 4056: { ! 4057: enum machine_mode mode = GET_MODE (operands[0]); ! 4058: ! 4059: /* All modes must be the same. */ ! 4060: if (! (mode == GET_MODE (operands[1]) ! 4061: && mode == GET_MODE (operands[2]) ! 4062: && mode == GET_MODE (operands[3]) ! 4063: && mode == GET_MODE (operands[4]) ! 4064: && mode == GET_MODE (operands[5]))) ! 4065: return 0; ! 4066: ! 4067: /* Both DFmode and SFmode should work. But using SFmode makes the ! 4068: assembler complain. Just turn it off for now. */ ! 4069: if (mode != DFmode) ! 4070: return 0; ! 4071: ! 4072: /* Only 2 real operands to the addition. One of the input operands must ! 4073: be the same as the output operand. */ ! 4074: if (! rtx_equal_p (operands[3], operands[4]) ! 4075: && ! rtx_equal_p (operands[3], operands[5])) ! 4076: return 0; ! 4077: ! 4078: /* Inout operand of add can not conflict with any operands from multiply. */ ! 4079: if (rtx_equal_p (operands[3], operands[0]) ! 4080: || rtx_equal_p (operands[3], operands[1]) ! 4081: || rtx_equal_p (operands[3], operands[2])) ! 4082: return 0; ! 4083: ! 4084: /* multiply can not feed into addition operands. */ ! 4085: if (rtx_equal_p (operands[4], operands[0]) ! 4086: || rtx_equal_p (operands[5], operands[0])) ! 4087: return 0; ! 4088: ! 4089: /* Passed. Operands are suitable for fmpyadd. */ ! 4090: return 1; ! 4091: } ! 4092: ! 4093: /* Returns 1 if the 6 operands specified in OPERANDS are suitable for ! 4094: use in fmpysub instructions. */ ! 4095: int ! 4096: fmpysuboperands(operands) ! 4097: rtx *operands; ! 4098: { ! 4099: enum machine_mode mode = GET_MODE (operands[0]); ! 4100: ! 4101: /* All modes must be the same. */ ! 4102: if (! (mode == GET_MODE (operands[1]) ! 4103: && mode == GET_MODE (operands[2]) ! 4104: && mode == GET_MODE (operands[3]) ! 4105: && mode == GET_MODE (operands[4]) ! 4106: && mode == GET_MODE (operands[5]))) ! 4107: return 0; ! 4108: ! 4109: /* Both DFmode and SFmode should work. But using SFmode makes the ! 4110: assembler complain. Just turn it off for now. */ ! 4111: if (mode != DFmode) ! 4112: return 0; ! 4113: ! 4114: /* Only 2 real operands to the subtraction. Subtraction is not a commutative ! 4115: operation, so operands[4] must be the same as operand[3]. */ ! 4116: if (! rtx_equal_p (operands[3], operands[4])) ! 4117: return 0; ! 4118: ! 4119: /* multiply can not feed into subtraction. */ ! 4120: if (rtx_equal_p (operands[5], operands[0])) ! 4121: return 0; ! 4122: ! 4123: /* Inout operand of sub can not conflict with any operands from multiply. */ ! 4124: if (rtx_equal_p (operands[3], operands[0]) ! 4125: || rtx_equal_p (operands[3], operands[1]) ! 4126: || rtx_equal_p (operands[3], operands[2])) ! 4127: return 0; ! 4128: ! 4129: /* Passed. Operands are suitable for fmpysub. */ ! 4130: return 1; ! 4131: } ! 4132: ! 4133: int ! 4134: plus_xor_ior_operator (op, mode) ! 4135: rtx op; ! 4136: enum machine_mode mode; ! 4137: { ! 4138: return (GET_CODE (op) == PLUS || GET_CODE (op) == XOR ! 4139: || GET_CODE (op) == IOR); ! 4140: } ! 4141: ! 4142: /* Return 1 if the given constant is 2, 4, or 8. These are the valid ! 4143: constants for shadd instructions. */ ! 4144: int ! 4145: shadd_constant_p (val) ! 4146: int val; ! 4147: { ! 4148: if (val == 2 || val == 4 || val == 8) ! 4149: return 1; ! 4150: else ! 4151: return 0; ! 4152: } ! 4153: ! 4154: /* Return 1 if OP is a CONST_INT with the value 2, 4, or 8. These are ! 4155: the valid constant for shadd instructions. */ ! 4156: int ! 4157: shadd_operand (op, mode) ! 4158: rtx op; ! 4159: enum machine_mode mode; ! 4160: { ! 4161: return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op))); ! 4162: } ! 4163: ! 4164: /* Return 1 if INSN branches forward. Should be using insn_addresses ! 4165: to avoid walking through all the insns... */ ! 4166: int ! 4167: forward_branch_p (insn) ! 4168: rtx insn; ! 4169: { ! 4170: rtx label = JUMP_LABEL (insn); ! 4171: ! 4172: while (insn) ! 4173: { ! 4174: if (insn == label) ! 4175: break; ! 4176: else ! 4177: insn = NEXT_INSN (insn); ! 4178: } ! 4179: ! 4180: return (insn == label); ! 4181: } ! 4182: ! 4183: /* Return 1 if OP is an equality comparison, else return 0. */ ! 4184: int ! 4185: eq_neq_comparison_operator (op, mode) ! 4186: rtx op; ! 4187: enum machine_mode mode; ! 4188: { ! 4189: return (GET_CODE (op) == EQ || GET_CODE (op) == NE); ! 4190: } ! 4191: ! 4192: /* Return 1 if OP is an operator suitable for use in a movb instruction. */ ! 4193: int ! 4194: movb_comparison_operator (op, mode) ! 4195: rtx op; ! 4196: enum machine_mode mode; ! 4197: { ! 4198: return (GET_CODE (op) == EQ || GET_CODE (op) == NE ! 4199: || GET_CODE (op) == LT || GET_CODE (op) == GE); ! 4200: } ! 4201: ! 4202: /* Return 1 if INSN is in the delay slot of a call instruction. */ ! 4203: int ! 4204: jump_in_call_delay (insn) ! 4205: rtx insn; ! 4206: { ! 4207: ! 4208: if (GET_CODE (insn) != JUMP_INSN) ! 4209: return 0; ! 4210: ! 4211: if (PREV_INSN (insn) ! 4212: && PREV_INSN (PREV_INSN (insn)) ! 4213: && GET_CODE (next_active_insn (PREV_INSN (PREV_INSN (insn)))) == INSN) ! 4214: { ! 4215: rtx test_insn = next_active_insn (PREV_INSN (PREV_INSN (insn))); ! 4216: ! 4217: return (GET_CODE (PATTERN (test_insn)) == SEQUENCE ! 4218: && XVECEXP (PATTERN (test_insn), 0, 1) == insn); ! 4219: ! 4220: } ! 4221: else ! 4222: return 0; ! 4223: } ! 4224:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.